Advanced Graphics and Data Visualization in R test

Lecture 06: Trees, Network Diagrams and Genomic Representations

0.1.0 An overview of Advanced Graphics and Data Visualization in R

“Advanced Graphics and Data Visualization in R” is brought to you by the Centre for the Analysis of Genome Evolution & Function’s (CAGEF) bioinformatics training initiative. This CSB1021 was developed to enhance the skills of students with basic backgrounds in R by focusing on available philosophies, methods, and packages for plotting scientific data. While the datasets and examples used in this course will be centred on SARS-CoV-2 epidemiological and genomic data, the lessons learned herein will be broadly applicable.

This lesson is the sixth and final lecture in a 6-part series. The aim for the end of this series is for students to recognize how to import, format, and display data based on their intended message and audience. The format and style of these visualizations will help to identify and convey the key message(s) from their experimental data.

The structure of the class is a code-along style in R markdown notebooks. At the start of each lecture, skeleton versions of the lecture will be provided for use on the University of Toronto datatools Hub so students can program along with the instructor.


0.2.0 Lecture objectives

This week will focus on commonly used visualizations related to genomic information. When working with sequencing data, you may commonly wish to compare sequences based on their relationships or relative similarity (trees), by their sequence identity in gene families or potential interactions (graphs), and or more directly their sequence motifs.

At the end of this lecture you will have covered the following topics

  1. Phylogenetic Trees
  2. Network graphs
  3. Genome sequences and markers

0.3.0 A legend for text format in R markdown

grey background - a package, function, code, command or directory. Backticks are also use for in-line code.
italics - an important term or concept or an individual file or folder
bold - heading or a term that is being defined
blue text - named or unnamed hyperlink

... - Within each coding cell this will indicate an area of code that students will need to complete for the code cell to run correctly.

Blue box: A key concept that is being introduced

Yellow box: Risk or caution

Green boxes: Recommended reads and resources to learn R

Red boxes: A comprehension question which may or may not involve a coding cell. You usually find these at the end of a section.


0.4.0 Lecture and data files used in this course

0.4.1 Weekly Lecture and skeleton files

Each week, new lesson files will appear within your RStudio folders. We are pulling from a GitHub repository using this Repository git-pull link. Simply click on the link and it will take you to the University of Toronto datatools Hub. You will need to use your UTORid credentials to complete the login process. From there you will find each week’s lecture files in the directory /2024-03-Adv_Graphics_R/Lecture_XX. You will find a partially coded skeleton.Rmd file as well as all of the data files necessary to run the week’s lecture.

Alternatively, you can download the R-Markdown Notebook (.Rmd) and data files from the RStudio server to your personal computer if you would like to run independently of the Toronto tools.

0.4.2 Live-coding HTML page

A live lecture version will be available at camok.github.io that will update as the lecture progresses. Be sure to refresh to take a look if you get lost!

0.4.3 Post-lecture PDFs

As mentioned above, at the end of each lecture there will be a completed version of the lecture code released as a PDF file under the Modules section of Quercus.

0.4.4 Data used in this lesson

Today’s datasets will focus on SARS-CoV-2 variant surveillance data from the which has been tracking published sequenced genomes for the appearance of new strains in North America.

0.4.4.1 Dataset 1: nextstrain_ncov_north-america_canada_timetree.nwk

This is a Newick format data set describing a phylogenetic tree of SARS-CoV-2 strain information.

0.4.4.2 Dataset 2: nextstrain_ncov_north-america_canada_metadata.tsv

Metadata that accompanies the first dataset. It links strain information back to as much geographical and related information as possible.


0.5.0 Packages used in this lesson

tidyverse which has a number of packages including dplyr, tidyr, stringr, forcats and ggplot2

viridis helps to create color-blind palettes for our data visualizations

RColorBrewer has some hlepful palettes that we’ll need to colour our data.

ggnewscale will be helpful in generating multiple colour palettes across increasingly complex plots.

treeio, tidytree, and ggtree will be used to help import, parse and plot phylogenetic trees.

tidygraph and ggraph will be used to generate network graph objects and plot them.

ggseqlogo is used for generating sequence logos related to sequence motifs.

qqman is a wrapper package for generating Manhattan plots.

lubridate and zoo help us to work with some date-based information.

# When do we start installing the packages
Sys.time()
[1] "2024-04-18 13:40:30 EDT"
# New bioconductor packages we haven't worked with before
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")

BiocManager::install("ggnewscale", update = FALSE)
'getOption("repos")' replaces Bioconductor standard repositories, see
'help("repositories", package = "BiocManager")' for details.
Replacement repositories:
    CRAN: https://cran.r-project.org
Bioconductor version 3.12 (BiocManager 1.30.22), R 4.0.5 (2021-03-31)
Warning: package(s) not installed when version(s) same as or greater than current; use
  `force = TRUE` to re-install: 'ggnewscale'
BiocManager::install("ggtree", update = FALSE)
'getOption("repos")' replaces Bioconductor standard repositories, see
'help("repositories", package = "BiocManager")' for details.
Replacement repositories:
    CRAN: https://cran.r-project.org
Bioconductor version 3.12 (BiocManager 1.30.22), R 4.0.5 (2021-03-31)
Warning: package(s) not installed when version(s) same as or greater than current; use
  `force = TRUE` to re-install: 'ggtree'
BiocManager::install("ggtreeExtra", update = FALSE)
'getOption("repos")' replaces Bioconductor standard repositories, see
'help("repositories", package = "BiocManager")' for details.
Replacement repositories:
    CRAN: https://cran.r-project.org
Bioconductor version 3.12 (BiocManager 1.30.22), R 4.0.5 (2021-03-31)
Warning: package(s) not installed when version(s) same as or greater than current; use
  `force = TRUE` to re-install: 'ggtreeExtra'
# New CRAN packages we haven't worked with before
install.packages("tidytree")
Warning: package 'tidytree' is in use and will not be installed
install.packages("ggseqlogo")
Warning: package 'ggseqlogo' is in use and will not be installed
install.packages("qqman")
Warning: package 'qqman' is in use and will not be installed
install.packages("ggraph")
Installing package into 'C:/Users/mokca/AppData/Local/R/win-library/4.0'
(as 'lib' is unspecified)
also installing the dependencies 'igraph', 'tidygraph', 'graphlayouts'

  There are binary versions available but the source versions are later:
             binary source needs_compilation
igraph        1.3.0  2.0.3              TRUE
tidygraph     1.2.1  1.3.1              TRUE
graphlayouts  0.8.0  1.1.1              TRUE
ggraph        2.0.5  2.2.1              TRUE
installing the source packages 'igraph', 'tidygraph', 'graphlayouts', 'ggraph'
Warning in install.packages("ggraph"): installation of package 'igraph' had
non-zero exit status
Warning in install.packages("ggraph"): installation of package 'tidygraph' had
non-zero exit status
Warning in install.packages("ggraph"): installation of package 'graphlayouts'
had non-zero exit status
Warning in install.packages("ggraph"): installation of package 'ggraph' had
non-zero exit status
install.packages("tidygraph")
Installing package into 'C:/Users/mokca/AppData/Local/R/win-library/4.0'
(as 'lib' is unspecified)
also installing the dependency 'igraph'

  There are binary versions available but the source versions are later:
          binary source needs_compilation
igraph     1.3.0  2.0.3              TRUE
tidygraph  1.2.1  1.3.1              TRUE
installing the source packages 'igraph', 'tidygraph'
Warning in install.packages("tidygraph"): installation of package 'igraph' had
non-zero exit status
Warning in install.packages("tidygraph"): installation of package 'tidygraph'
had non-zero exit status
# When do we finish?
Sys.time()
[1] "2024-04-18 13:42:08 EDT"
# Packages to help tidy our data
library(tidyverse)
library(readxl)

# Packages for the graphical analysis section
library(viridis)
library(RColorBrewer)
library(ggnewscale)

# Packages for today's lecture about trees
library(tidytree)
library(ggtree)
Error: package or namespace load failed for 'ggtree' in loadNamespace(j <- i[[1L]], c(lib.loc, .libPaths()), versionCheck = vI[[j]]):
 there is no package called 'aplot'
library(ggtreeExtra)
library(treeio)

# Packages for graphs
library(ggraph)
Error in library(ggraph): there is no package called 'ggraph'
library(tidygraph)
Error in library(tidygraph): there is no package called 'tidygraph'
# Packages for sequence logos
library(ggseqlogo)

# Packages for Manhattan plots
library(qqman)

# Date calculation helpers
library(lubridate)
library(zoo)

1.0.0 What’s in a name? A phylogenetic tree is just a rebranded dendrogram

Dendrograms, cladograms, and phylogenetic trees all share a similar structure with some modifications. All are represented in branching tree structures that are used to represent the relationships among the leaves, also known as tips. Branches along the tree may also be referred to as edges.

Leaves can represent different species, strains, sequences or multi-dimensional values. Leaves are connected by branches to their nearest neighbours or relatives. As you move backwards along the tree, you encounter internal nodes which can connect directly to more tips or more nodes. The distance between tips and nodes on a phylogenetic tree, represent a relative distance that may be defined as some type of evolutionary distance, time, or simple euclidean distance. In a cladogram, however, distances along branches have no meaning and only define the presence of a relationship.

In tree terminology, it is helpful to define a few more terms:

Term Description
Root A tree is rooted when it defines a common ancestor for all tips in the tree. This could be considered the start or entry point for the tree
Most recent common ancester (MRCA) This is an internal node that collectively joins two or more tips.
Clade A group of taxa (tips) that includes a common ancestor and all of its descendants.
Scaling This indicates that the branch lengths do represent a distance metric, whereas, an unscaled tree may have even-length branches not representative of evolutionary/relationship distances.

Diagram of phylogenetic tree terms from 10 differences between cladograms and phylogenetics trees


1.1.0 Where does tree data come from?

As we saw last week, clustering analysis such as that from hclust() can create dendrogram data. We plotted this in a couple of ways by itself using fviz_dend() from the factoextra package and with Heatmap() from the ComplexHeatmap package. In the case of our first foray, we were looking at the “relationships” between samples by indicating how similar they were based on the characteristics in their various features.

Depending on the software used, trees can be represented in a number of formats, some of which are described below.

Format Description
Newick The standard used to represent trees in a computer-readable format. Trees are encoded in a parentheses-closure format where each tip takes the form of “taxa:branch-length
and pairs of tips are separated by a comma , and enclosed by parentheses (). Internal nodes branch lengths are defined outside the parentheses with “():branch-length
NEXUS/phylip Incorporates the Newick tree with separate related data that is partitioned into different blocks. Blocks are started with “BEGIN <BLOCK NAME>;” and closed with “END;
NHX New Hampshire eXtended format is also based on Newick format but instead of code blocks uses a tagging system for each node extending the format to “taxa:branch-length[&&NHX:<tag information>]

1.2.0 Using read.tree() from the treeio package

When opening tree files, depending on the format, you can use the treeio package and one of it’s many parsing functions but the simplest way to avoid figuring out how to open a tree file, is to just use the read.tree() function. This will determine the appropriate file type and parse through the many different tree formats before depositing the data into a phylo object.

Today we’ll be working with a dataset from the Auspice COVID-19 North American dataset maintained by Finlay Maguire. Unfortunately this dataset is no longer available but we have an older copy with a fair amount of metadata.

Let’s begin by opening up the tree data and some related metadata before taking a look under the hood.

# Import our Newick Tree
SC2_variants_time.phylo <- read.tree("./data/nextstrain_ncov_north-america_canada_timetree.nwk")

# Import our metadata
SC2_metadata.df <- read_tsv("data/nextstrain_ncov_north-america_canada_metadata.tsv")
Rows: 6627 Columns: 24
-- Column specification -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Delimiter: "\t"
chr  (21): Strain, GISAID clade, Nextstrain clade, Clade, Country, Admin Div...
dbl   (1): Age
lgl   (1): url
date  (1): Collection Data

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
# What does the tree look like?
str(SC2_variants_time.phylo)
List of 6
 $ edge       : int [1:12776, 1:2] 6628 6628 6629 6630 6631 6630 6632 6632 6633 6629 ...
 $ edge.length: num [1:12776] 0.0757 0.0848 0.0336 0.0501 0 ...
 $ Nnode      : int 6150
 $ node.label : chr [1:6150] "NODE_0008606" "NODE_0000001" "NODE_0000061" "USA/MA1/2020_travel_history" ...
 $ tip.label  : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
 $ root.edge  : num 2020
 - attr(*, "class")= chr "phylo"
 - attr(*, "order")= chr "cladewise"
SC2_variants_time.phylo

Phylogenetic tree with 6627 tips and 6150 internal nodes.

Tip labels:
  Wuhan/WH01/2019, USA/MA1/2020, Canada/ON_ON-VIDO-01-2/2020, Canada/ON_VIDO-01/2020, USA/CA-CDC-5/2020, Taiwan/CGMH-CGU-02/2020, ...
Node labels:
  NODE_0008606, NODE_0000001, NODE_0000061, USA/MA1/2020_travel_history, NODE_0000062, Canada/ON_VIDO-01/2020_travel_history, ...

Rooted; includes branch lengths.

1.2.1 The phylo object is a simple list

From the looks of our structure, after reading in our Newick file, we have a list with six elements. They are pretty clearly named with edge information (a matrix of paired numbers), edge lengths, a count of the number of nodes (ie internal points where branches bifurcate. While mostly just numbers, some of these node.label values appear to be based on travel history information. All of the 6,627 tip.label values correspond to 6,627 SARS-CoV-2 strain names found in the metadata file as well.


1.2.2 Convert the tree object to a tibble for adding external information

Suppose we want to combine some information from our metadata with our tree? We can then use this information to colour, shape, and bring more visual order to our tree. Working with the tree, however, can be a little hard given that we have lists of different lengths representing different portions of the tree.

In our case, we only have tree tip information that we want to add to and the easiest way to work would be if it was in a table format. The package tidytree provides a function as_tibble that will parse and convert the phylo format to a tbl_tree object, which is a kind of tidy data frame.

Let’s convert our variant phylo object to something we can work with.

# Convert the phylo object using as_tibble
SC2_variants_time.tb <- as_tibble(SC2_variants_time.phylo)

# Check out the structure now
str(SC2_variants_time.tb)
tbl_tree [12,777 x 4] (S3: tbl_tree/tbl_df/tbl/data.frame)
 $ parent       : int [1:12777] 6628 6631 6632 6633 6636 6635 6638 6638 6639 6640 ...
 $ node         : int [1:12777] 1 2 3 4 5 6 7 8 9 10 ...
 $ branch.length: num [1:12777] 0.0757 0 0.0077 0 0 ...
 $ label        : chr [1:12777] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
head(SC2_variants_time.tb)

1.3.0 Updating our tree with external information

As you can see our SC2_variants_time.tb object is a simple data frame now represented as a 4-column table with 12,777 rows. The edge data is encoded between the parent and node columns with branch.length to define the length of each edge. We also have all of the tip and node names stored under the label column. With the tree in this format, we can now treat the tree like a tibble and join additional information to the tree.

Rules to keep in mind:

  1. Don’t lose any node information from your original tree structure. Losing “observations” is essentially losing edges of your tree!

  2. Try to work with a complete dataset of information although sometimes it doesn’t make sense that you would have it all.

  3. Convert your tibble back to a tree format when you’re done with as.treedata()

You may have information about the leaves or tips of your tree but by default there is no real internal node information when it comes to sample origins or lineage. When joining information to the tables, use the correct direction *_join() to avoid data loss. A full_join() is probably the safest.

Let’s take a quick look at our metadata and identify what we’re interested in.

# Look at the metadata, which bits of information would be nice to add?
str(SC2_metadata.df, give.attr = FALSE)
spc_tbl_ [6,627 x 24] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Strain                    : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
 $ GISAID clade              : chr [1:6627] "L" "L" "L" "L" ...
 $ Nextstrain clade          : chr [1:6627] "19A" "19A" "19A" "19A" ...
 $ Age                       : num [1:6627] 44 21 NA NA 54 NA 47 47 23 55 ...
 $ Clade                     : chr [1:6627] "19A" "19A" "19A" "19A" ...
 $ Country                   : chr [1:6627] "Asia" "USA" "Canada" "Canada" ...
 $ Admin Division            : chr [1:6627] "Asia" "Massachusetts" "Ontario" "Ontario" ...
 $ Admin Division of exposure: chr [1:6627] "Asia" "Asia" "Ontario" "Asia" ...
 $ gisaid_epi_isl            : chr [1:6627] "EPI_ISL_406798" "EPI_ISL_409067" "EPI_ISL_425177" "EPI_ISL_413015" ...
 $ Host                      : chr [1:6627] "Human" "Human" "Human" "Human" ...
 $ Originating Lab           : chr [1:6627] "General Hospital of Central Theater Command of People's Liberation Army of China" "Massachusetts Department of Public Health" "Public Health Ontario" "Public Health Ontario Laboratory" ...
 $ PANGO lineage             : chr [1:6627] "B" "B" "B" "B" ...
 $ Submission Date           : chr [1:6627] "Older" "Older" "Older" "Older" ...
 $ Region                    : chr [1:6627] "Asia" "North America" "North America" "North America" ...
 $ Region of exposure        : chr [1:6627] "Asia" "Asia" "North America" "Asia" ...
 $ Sex                       : chr [1:6627] "Male" "Male" "Male" "Male" ...
 $ strain                    : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
 $ Emerging Nextstrain clade : chr [1:6627] "19A" "19A" "19A" "19A" ...
 $ Submitting Lab            : chr [1:6627] "BGI & Institute of Microbiology, Chinese Academy of Sciences & Shandong First Medical University & Shandong Aca"| __truncated__ "Pathogen Discovery, Respiratory Viruses Branch, Division of Viral Diseases, Centers for Disease Control and Prevention" "Public Health Agency of Canada - National Microbiology Laboratory" "National Microbiology Laboratory" ...
 $ url                       : logi [1:6627] NA NA NA NA NA NA ...
 $ Collection Data           : Date[1:6627], format: "2019-12-26" "2020-01-29" ...
 $ Author                    : chr [1:6627] "Weijun Chen et al (https://dx.doi.org/10.1016/S0140-6736(20)30251-8)" "Clinton R. Paden et al (https://dx.doi.org/10.1101/2020.03.09.20032896)" "Amrit S. Boese et al" "Shari Tyson et al" ...
 $ Country of exposure       : chr [1:6627] NA "Asia" NA "Asia" ...
 $ Location                  : chr [1:6627] NA "Boston" NA NA ...
# Check out some of the values for these variables

# Metadata information on the samples
unique(SC2_metadata.df$Country)
 [1] "Asia"               "USA"                "Canada"            
 [4] "Europe"             "Guatemala"          "Panama"            
 [7] "Africa"             "Dominican Republic" "Costa Rica"        
[10] "South America"      "Oceania"            "Jamaica"           
[13] "Mexico"             "Barbados"           "Guadeloupe"        
[16] "Saint Martin"       "Bermuda"            "Sint Maarten"      
[19] "Saint Barthélemy"   "Belize"             "Saint Lucia"       
[22] "El Salvador"        "Cuba"              
Asia

USA

Canada

Europe

Guatemala

Panama

Africa

Dominican Republic

Costa Rica

South America

Oceania

Jamaica

Mexico

Barbados

Guadeloupe

Saint Martin

Bermuda

Sint Maarten

Saint Barthélemy

Belize

Saint Lucia

El Salvador

Cuba
unique(SC2_metadata.df$Age)
  [1]  44  21  NA  54  47  23  55  62  49  50  43  25  26  33  38   9  73  64
 [19]  57  58   0  28  70  71  20  93  36  37  40  56  89  45  34  53  94  95
 [37]  86  84  60  90  27  30  59  41  46  52  51  32  24  35  29  61  79  66
 [55]  78  11   2  65  75  31  48  74  68  67  63  77  42  22  12   5  17  80
 [73]  16  39  82   8   7  88  76  69  19  87   3  14  18   1  81  15   6  10
 [91]  85   4  98  91  72  99  13  92  83  96 104
# Metadata information on the genomes
unique(SC2_metadata.df$'GISAID clade')
 [1] "L"   "S"   "O"   "V"   "GR"  "G"   "GV"  "GH"  "GRY" NA   
L

S

O

V

GR

G

GV

GH

GRY

NA```



<!-- rnb-output-end -->

<!-- rnb-source-begin eyJkYXRhIjoidW5pcXVlKFNDMl9tZXRhZGF0YS5kZiQnTmV4dHN0cmFpbiBjbGFkZScpIn0= -->

```r
unique(SC2_metadata.df$'Nextstrain clade')
 [1] "19A"         "19B"         NA            "20A"         "20B"        
 [6] "20E (EU1)"   "20A.EU2"     "20C"         "20G"         "20H/501Y.V2"
[11] "20D"         "20J/501Y.V3" "20F"         "20I/501Y.V1"
19A

19B

NA
20A

20B

20E (EU1)

20A.EU2

20C

20G

20H/501Y.V2

20D

20J/501Y.V3

20F

20I/501Y.V1
unique(SC2_metadata.df$Clade)
 [1] "19A"         "19B"         "20A"         "20E (EU1)"   "20C"        
 [6] "20G"         "20H/501Y.V2" "20B"         "20D"         "20J/501Y.V3"
[11] "20F"         "20I/501Y.V1"
19A

19B

20A

20E (EU1)

20C

20G

20H/501Y.V2

20B

20D

20J/501Y.V3

20F

20I/501Y.V1
unique(SC2_metadata.df$'PANGO lineage')
  [1] "B"           "A"           "A.6"         "A.2"         "A.2.4"      
  [6] "A.2.5"       "A.5"         "A.1"         "A.21"        "A.18"       
 [11] "A.12"        "A.28"        "A.19"        "A.25"        "A.23"       
 [16] "A.23.1"      "B.12"        "B.56"        "B.4.7"       "B.31"       
 [21] "B.61"        "B.40"        "B.35"        "B.28"        "B.11"       
 [26] "B.55"        "B.27"        "B.3"         "B.4"         "B.4.8"      
 [31] "B.4.2"       "B.53"        "B.6"         "B.1"         "B.6.6"      
 [36] "B.6.1"       "B.6.2"       "B.6.8"       "B.1.256"     "B.1.1"      
 [41] "B.1.1.420"   "B.1.240"     "B.1.205"     "B.1.128"     "B.1.221"    
 [46] "B.1.223"     "B.1.214.2"   "B.1.214"     "B.1.146"     "B.1.147"    
 [51] "B.1.104"     "B.1.391"     "B.1.380"     "B.1.203"     "B.1.540"    
 [56] "B.1.416"     "B.1.600"     "B.1.160"     "B.1.195"     "B.1.609"    
 [61] "B.1.561"     "B.1.232"     "B.1.558"     "B.1.239"     "B.1.237"    
 [66] "B.1.597"     "B.1.480"     "B.1.397"     "B.1.408"     "B.1.243"    
 [71] "B.1.240.2"   "B.1.240.1"   "B.1.524"     "B.1.179"     "B.1.1.298"  
 [76] "B.1.400"     "B.1.398"     "B.1.236"     "B.1.234"     "B.1.459"    
 [81] "B.1.131"     "B.1.525"     "B.1.563"     "B.1.411"     "B.1.379"    
 [86] "B.1.177.31"  "B.1.177"     "B.1.177.73"  "B.1.177.11"  "B.1.177.14" 
 [91] "B.1.177.15"  "AA.1"        "B.1.177.44"  "B.1.177.39"  "B.1.177.40" 
 [96] "B.1.177.35"  "B.1.177.82"  "B.1.177.87"  "B.1.177.75"  "B.1.177.45" 
[101] "B.1.177.28"  "B.1.177.32"  "B.1.177.37"  "B.1.177.72"  "B.1.117.2"  
[106] "B.1.177.4"   "B.1.177.53"  "W.2"         "W.1"         "B.1.177.50" 
[111] "B.1.177.81"  "B.1.177.58"  "B.1.177.55"  "B.1.177.56"  "B.1.177.57" 
[116] "B.1.177.51"  "B.1.177.52"  "B.1.177.21"  "B.1.177.77"  "B.1.177.6"  
[121] "B.1.177.7"   "B.1.177.86"  "B.1.177.8"   "B.1.177.62"  "B.1.177.60" 
[126] "U.3"         "B.1.177.79"  "B.1.9.5"     "B.1.9"       "B.1.439"    
[131] "B.1.111"     "B.1.162"     "B.1.438"     "B.1.164"     "B.1.36"     
[136] "B.1.441"     "B.1.36.35"   "B.1.468"     "B.1.462"     "B.1.471"    
[141] "B.1.469"     "B.1.466"     "B.1.466.2"   "B.1.470"     "B.1.160.16" 
[146] "B.1.456"     "B.1.36.29"   "B.1.36.31"   "B.1.36.1"    "B.1.36.8"   
[151] "B.1.36.26"   "B.1.36.34"   "B.1.36.7"    "B.1.36.16"   "B.1.36.36"  
[156] "B.1.36.38"   "B.1.36.37"   "B.1.36.18"   "B.1.36.10"   "B.1.476"    
[161] "B.1.281"     "B.1.273"     "B.1.544"     "B.1.270"     "B.1.277"    
[166] "B.1.279"     "B.1.314"     "B.1.367"     "B.1.370"     "B.1.413"    
[171] "B.1.316"     "B.1.3"       "B.1.509"     "B.1.504"     "B.1.362"    
[176] "B.1.499"     "B.1.615"     "B.1.611"     "B.1.505"     "B.1.422"    
[181] "B.1.596.1"   "B.1.362.2"   "B.1.369"     "B.1.564"     "B.1.564.1"  
[186] "B.1.369.1"   "B.1.1.1"     "B.1.428"     "B.1.356"     "B.1.601"    
[191] "B.1.562"     "B.1.360"     "B.1.311"     "B.1.598"     "B.1.350"    
[196] "B.1.426"     "B.1.265"     "B.1.336"     "B.1.589"     "B.1.567"    
[201] "B.1.588.1"   "B.1.595"     "B.1.596"     "B.1.2"       "B.1.349"    
[206] "B.1.366"     "B.1.576"     "B.1.603"     "B.1.526.2"   "B.1.526"    
[211] "B.1.291"     "B.1.575"     "B.1.428.3"   "B.1.428.1"   "B.1.428.2"  
[216] "B.1.427"     "B.1.429"     "B.1.495"     "B.1.517"     "B.1.324"    
[221] "B.1.298"     "B.1.573"     "B.1.320"     "B.1.361"     "B.1.582"    
[226] "B.1.612"     "B.1.351"     "B.1.1.255"   "B.1.1.430"   "B.1.1.306"  
[231] "AE.7"        "AE.2"        "AE.5"        "AE.6"        "AE.4"       
[236] "AE.8"        "B.1.153"     "B.1.329"     "B.1.535"     "B.1.313"    
[241] "B.1.170"     "B.1.22.1"    "B.1.201"     "B.1.227"     "B.1.415"    
[246] "B.1.94"      "B.1.1.519"   "B.1.1.222"   "B.1.552"     "B.1.199"    
[251] "B.1.178"     "B.1.220"     "B.1.6"       "B.1.84"      "B.1.1.170"  
[256] "B.1.93"      "B.1.91"      "B.1.219"     "B.1.211"     "B.1.388"    
[261] "B.1.210"     "B.1.555"     "B.1.206"     "B.1.260"     "B.1.420"    
[266] "B.1.192"     "B.1.258"     "B.1.258.17"  "B.1.242"     "B.1.126"    
[271] "B.1.378"     "B.1.565"     "B.1.139"     "B.1.8"       "B.1.78"     
[276] "B.1.396"     "B.1.560"     "B.1.527"     "B.1.142"     "B.1.393"    
[281] "B.1.187"     "B.1.149"     "B.1.549"     "B.1.545"     "B.1.189"    
[286] "B.1.528"     "B.1.23"      "B.1.67"      "B.1.547"     "P.2"        
[291] "B.1.1.28"    "B.1.390"     "B.1.395"     "B.1.530"     "C.35"       
[296] "B.1.233"     "B.1.543"     "B.6.7"       "B.1.1.369"   "B.1.1.372"  
[301] "C.8"         "C.36"        "C.36.1"      "C.16"        "C.23"       
[306] "C.1.1"       "B.1.1.375"   "C.2.1"       "C.12"        "B.1.1.161"  
[311] "B.1.1.87"    "B.1.1.192"   "B.1.1.71"    "B.1.1.10"    "L.3"        
[316] "L.1"         "B.1.1.176"   "B.1.1.398"   "B.1.1.417"   "B.1.1.63"   
[321] "B.1.1.422"   "B.1.1.487"   "B.1.1.312"   "B.1.1.331"   "B.1.1.58"   
[326] "B.1.1.277"   "K.1"         "B.1.1.30"    "B.1.1.516"   "B.1.1.50"   
[331] "B.1.1.33"    "N.2"         "N.3"         "N.7"         "N.5"        
[336] "N.8"         "N.4"         "B.1.1.409"   "B.1.1.61"    "B.1.1.220"  
[341] "R.1"         "B.1.1.317"   "B.1.1.464.1" "B.1.1.316"   "B.1.1.254"  
[346] "B.1.1.267"   "B.1.1.241"   "B.1.1.25"    "B.1.1.181"   "B.1.1.434"  
[351] "B.1.1.243"   "B.1.1.159"   "B.1.1.411"   "B.1.1.232"   "B.1.1.459"  
[356] "B.1.1.111"   "B.1.1.294"   "B.1.1.373"   "B.1.1.413"   "B.1.1.354"  
[361] "B.1.1.54"    "B.1.1.325"   "B.1.1.399"   "B.1.1.46"    "B.1.1.274"  
[366] "B.1.1.410"   "B.1.1.315"   "AD.2"        "B.1.1.142"   "B.1.1.485"  
[371] "B.1.1.200"   "B.1.1.339"   "AL.1"        "B.1.1.163"   "B.1.1.378"  
[376] "B.1.1.39"    "B.1.1.440"   "B.1.1.297"   "B.1.1.397"   "B.1.1.207"  
[381] "B.1.1.391"   "B.1.1.348"   "B.1.1.121"   "B.1.1.512"   "B.1.1.136"  
[386] "B.1.1.288"   "B.1.1.368"   "B.1.1.328"   "B.1.1.324"   "B.1.1.360"  
[391] "B.1.1.359"   "B.1.1.374"   "B.1.1.12"    "B.1.1.407"   "B.1.1.500"  
[396] "B.1.1.432"   "B.1.301"     "B.1.1.37"    "B.1.1.396"   "B.1.1.330"  
[401] "B.1.1.280"   "B.1.1.141"   "B.1.1.273"   "B.1.1.514"   "B.1.1.311"  
[406] "B.1.1.377"   "B.1.1.351"   "B.1.1.350"   "B.1.1.266"   "B.1.1.225"  
[411] "B.1.1.371"   "B.1.1.438"   "B.1.1.153"   "B.1.1.134"   "B.1.1.439"  
[416] "B.1.1.152"   "B.1.1.275"   "B.1.406"     "B.1.1.261"   "P.1"        
[421] "B.1.1.263"   "B.1.1.349"   "B.1.1.214"   "B.1.326"     "B.1.1.269"  
[426] "D.2"         "B.1.1.381"   "B.1.1.448"   "B.1.1.388"   "B.1.1.157"  
[431] "B.1.1.394"   "B.1.1.218"   "B.1.1.27"    "B.1.1.70"    "B.1.1.204"  
[436] "B.1.1.301"   "B.1.1.326"   "B.1.1.120"   "B.1.1.389"   "B.1.1.401"  
[441] "B.1.1.404"   "B.1.1.416"   "B.1.1.216"   "AM.1"        "AM.4"       
[446] "B.1.1.34"    "B.1.1.198"   "B.1.1.319"   "B.1.1.7"     NA           
B

A

A.6

A.2

A.2.4

A.2.5

A.5

A.1

A.21

A.18

A.12

A.28

A.19

A.25

A.23

A.23.1

B.12

B.56

B.4.7

B.31

B.61

B.40

B.35

B.28

B.11

B.55

B.27

B.3

B.4

B.4.8

B.4.2

B.53

B.6

B.1

B.6.6

B.6.1

B.6.2

B.6.8

B.1.256

B.1.1

B.1.1.420

B.1.240

B.1.205

B.1.128

B.1.221

B.1.223

B.1.214.2

B.1.214

B.1.146

B.1.147

B.1.104

B.1.391

B.1.380

B.1.203

B.1.540

B.1.416

B.1.600

B.1.160

B.1.195

B.1.609

B.1.561

B.1.232

B.1.558

B.1.239

B.1.237

B.1.597

B.1.480

B.1.397

B.1.408

B.1.243

B.1.240.2

B.1.240.1

B.1.524

B.1.179

B.1.1.298

B.1.400

B.1.398

B.1.236

B.1.234

B.1.459

B.1.131

B.1.525

B.1.563

B.1.411

B.1.379

B.1.177.31

B.1.177

B.1.177.73

B.1.177.11

B.1.177.14

B.1.177.15

AA.1

B.1.177.44

B.1.177.39

B.1.177.40

B.1.177.35

B.1.177.82

B.1.177.87

B.1.177.75

B.1.177.45

B.1.177.28

B.1.177.32

B.1.177.37

B.1.177.72

B.1.117.2

B.1.177.4

B.1.177.53

W.2

W.1

B.1.177.50

B.1.177.81

B.1.177.58

B.1.177.55

B.1.177.56

B.1.177.57

B.1.177.51

B.1.177.52

B.1.177.21

B.1.177.77

B.1.177.6

B.1.177.7

B.1.177.86

B.1.177.8

B.1.177.62

B.1.177.60

U.3

B.1.177.79

B.1.9.5

B.1.9

B.1.439

B.1.111

B.1.162

B.1.438

B.1.164

B.1.36

B.1.441

B.1.36.35

B.1.468

B.1.462

B.1.471

B.1.469

B.1.466

B.1.466.2

B.1.470

B.1.160.16

B.1.456

B.1.36.29

B.1.36.31

B.1.36.1

B.1.36.8

B.1.36.26

B.1.36.34

B.1.36.7

B.1.36.16

B.1.36.36

B.1.36.38

B.1.36.37

B.1.36.18

B.1.36.10

B.1.476

B.1.281

B.1.273

B.1.544

B.1.270

B.1.277

B.1.279

B.1.314

B.1.367

B.1.370

B.1.413

B.1.316

B.1.3

B.1.509

B.1.504

B.1.362

B.1.499

B.1.615

B.1.611

B.1.505

B.1.422

B.1.596.1

B.1.362.2

B.1.369

B.1.564

B.1.564.1

B.1.369.1

B.1.1.1

B.1.428

B.1.356

B.1.601

B.1.562

B.1.360

B.1.311

B.1.598

B.1.350

B.1.426

B.1.265

B.1.336

B.1.589

B.1.567

B.1.588.1

B.1.595

B.1.596

B.1.2

B.1.349

B.1.366

B.1.576

B.1.603

B.1.526.2

B.1.526

B.1.291

B.1.575

B.1.428.3

B.1.428.1

B.1.428.2

B.1.427

B.1.429

B.1.495

B.1.517

B.1.324

B.1.298

B.1.573

B.1.320

B.1.361

B.1.582

B.1.612

B.1.351

B.1.1.255

B.1.1.430

B.1.1.306

AE.7

AE.2

AE.5

AE.6

AE.4

AE.8

B.1.153

B.1.329

B.1.535

B.1.313

B.1.170

B.1.22.1

B.1.201

B.1.227

B.1.415

B.1.94

B.1.1.519

B.1.1.222

B.1.552

B.1.199

B.1.178

B.1.220

B.1.6

B.1.84

B.1.1.170

B.1.93

B.1.91

B.1.219

B.1.211

B.1.388

B.1.210

B.1.555

B.1.206

B.1.260

B.1.420

B.1.192

B.1.258

B.1.258.17

B.1.242

B.1.126

B.1.378

B.1.565

B.1.139

B.1.8

B.1.78

B.1.396

B.1.560

B.1.527

B.1.142

B.1.393

B.1.187

B.1.149

B.1.549

B.1.545

B.1.189

B.1.528

B.1.23

B.1.67

B.1.547

P.2

B.1.1.28

B.1.390

B.1.395

B.1.530

C.35

B.1.233

B.1.543

B.6.7

B.1.1.369

B.1.1.372

C.8

C.36

C.36.1

C.16

C.23

C.1.1

B.1.1.375

C.2.1

C.12

B.1.1.161

B.1.1.87

B.1.1.192

B.1.1.71

B.1.1.10

L.3

L.1

B.1.1.176

B.1.1.398

B.1.1.417

B.1.1.63

B.1.1.422

B.1.1.487

B.1.1.312

B.1.1.331

B.1.1.58

B.1.1.277

K.1

B.1.1.30

B.1.1.516

B.1.1.50

B.1.1.33

N.2

N.3

N.7

N.5

N.8

N.4

B.1.1.409

B.1.1.61

B.1.1.220

R.1

B.1.1.317

B.1.1.464.1

B.1.1.316

B.1.1.254

B.1.1.267

B.1.1.241

B.1.1.25

B.1.1.181

B.1.1.434

B.1.1.243

B.1.1.159

B.1.1.411

B.1.1.232

B.1.1.459

B.1.1.111

B.1.1.294

B.1.1.373

B.1.1.413

B.1.1.354

B.1.1.54

B.1.1.325

B.1.1.399

B.1.1.46

B.1.1.274

B.1.1.410

B.1.1.315

AD.2

B.1.1.142

B.1.1.485

B.1.1.200

B.1.1.339

AL.1

B.1.1.163

B.1.1.378

B.1.1.39

B.1.1.440

B.1.1.297

B.1.1.397

B.1.1.207

B.1.1.391

B.1.1.348

B.1.1.121

B.1.1.512

B.1.1.136

B.1.1.288

B.1.1.368

B.1.1.328

B.1.1.324

B.1.1.360

B.1.1.359

B.1.1.374

B.1.1.12

B.1.1.407

B.1.1.500

B.1.1.432

B.1.301

B.1.1.37

B.1.1.396

B.1.1.330

B.1.1.280

B.1.1.141

B.1.1.273

B.1.1.514

B.1.1.311

B.1.1.377

B.1.1.351

B.1.1.350

B.1.1.266

B.1.1.225

B.1.1.371

B.1.1.438

B.1.1.153

B.1.1.134

B.1.1.439

B.1.1.152

B.1.1.275

B.1.406

B.1.1.261

P.1

B.1.1.263

B.1.1.349

B.1.1.214

B.1.326

B.1.1.269

D.2

B.1.1.381

B.1.1.448

B.1.1.388

B.1.1.157

B.1.1.394

B.1.1.218

B.1.1.27

B.1.1.70

B.1.1.204

B.1.1.301

B.1.1.326

B.1.1.120

B.1.1.389

B.1.1.401

B.1.1.404

B.1.1.416

B.1.1.216

AM.1

AM.4

B.1.1.34

B.1.1.198

B.1.1.319

B.1.1.7

NA```



<!-- rnb-output-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


------------------------------------------------------------------------

### 1.3.1 Pick the tip attributes that are most interesting or relevant

For our purposes it looks like we will want to explore our data with:

-   GISAID clade: A **g**lobal **i**nitiative on **s**haring **a**vian **i**nfluenza **d**ata scientific consortium of clade naming.

-   Nextstrain clade: Computationally labeled clade information based on the [Nextstrain](https://nextstrain.org/) criteria and analysis of new and continuing strains from COVID genomic data.

-   Emerging Nextstrain clade.

-   PANGO lineage: a dynamic nomenclature of [SARS-CoV-2 lineages](https://en.wikipedia.org/wiki/Phylogenetic_Assignment_of_Named_Global_Outbreak_Lineages).

-   Country: The country where the sample was collected.

-   Admin Division: geographic location of the particular strain/case.

-   Age: the age of the patients from which the sample was collected (if included)

-   Collection Data: the date the data was published or collected.

-   Strain: the name of the strain which we'll need for merging with our tree structure.

Let's pull that information down and add it to our `tbl_tree` before converting it to a `treedata` object.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjpbIiMgQWRkIHBoeWxvZ2VuZXRpYyBpbmZvcm1hdGlvbiB0byBvdXIgdHJlZSIsIlNDMl92YXJpYW50c190aW1lLnRyZWUgPC1cblxuICAjIFBhc3MgYWxvbmcgdGhlIG1ldGFkYXRhXG4gIFNDMl9tZXRhZGF0YS5kZiAlPiUgXG4gIFxuICAjIFNlbGVjdCBqdXN0IGEgaGFuZGZ1bCBvZiBhdHRyaWJ1dGVzIHRvIGdvIHRvIHRoZSB0cmVlXG4gIHNlbGVjdChTdHJhaW4sICdHSVNBSUQgY2xhZGUnLCAnRW1lcmdpbmcgTmV4dHN0cmFpbiBjbGFkZScsIFxuICAgICAgICAgJ1BBTkdPIGxpbmVhZ2UnLCBDb3VudHJ5LCAnQWRtaW4gRGl2aXNpb24nLCAnQ29sbGVjdGlvbiBEYXRhJywgQWdlKSAlPiUgXG4gIFxuICByZW5hbWUoc3RyYWluID0gU3RyYWluLFxuICAgICAgICAgR0lTQUlEID0gJ0dJU0FJRCBjbGFkZScsIFxuICAgICAgICAgZW1lcmdpbmdfbmV4dHN0cmFpbiA9ICdFbWVyZ2luZyBOZXh0c3RyYWluIGNsYWRlJyxcbiAgICAgICAgIFBBTkdPID0gJ1BBTkdPIGxpbmVhZ2UnLFxuICAgICAgICAgc3RyYWluX2NvdW50cnkgPSBDb3VudHJ5LFxuICAgICAgICAgc3RyYWluX2RpdmlzaW9uID0gJ0FkbWluIERpdmlzaW9uJywgXG4gICAgICAgICBzdHJhaW5fZGF0ZSA9ICdDb2xsZWN0aW9uIERhdGEnKSAlPiUgXG4gIFxuICAjIGZ1bGxfam9pbiB0byBvdXIgdHJlZSB0byBlbnN1cmUgbm8gZGF0YSBpcyBsb3N0IGFsdGhvdWdoIHlvdSBjb3VsZCBhbHNvIGNhcmVmdWxseSB1c2UgYSBsZWZ0X2pvaW5cbiAgZnVsbF9qb2luKHg9U0MyX3ZhcmlhbnRzX3RpbWUudGIsIHk9LiwgYnk9YyhcImxhYmVsXCIgPSBcInN0cmFpblwiKSkgJT4lIFxuICBcbiAgIyBDb252ZXJ0IHRvIGEgdGlkeXRyZWUgZm9ybWF0XG4gIGFzLnRyZWVkYXRhKCkiLCIiLCIjIExvb2sgYXQgdGhlIHJlc3VsdGluZyBzdHJ1Y3R1cmUiLCJzdHIoU0MyX3ZhcmlhbnRzX3RpbWUudHJlZSkiXX0= -->

```r
# Add phylogenetic information to our tree
SC2_variants_time.tree <-

  # Pass along the metadata
  SC2_metadata.df %>% 
  
  # Select just a handful of attributes to go to the tree
  select(Strain, 'GISAID clade', 'Emerging Nextstrain clade', 
         'PANGO lineage', Country, 'Admin Division', 'Collection Data', Age) %>% 
  
  rename(strain = Strain,
         GISAID = 'GISAID clade', 
         emerging_nextstrain = 'Emerging Nextstrain clade',
         PANGO = 'PANGO lineage',
         strain_country = Country,
         strain_division = 'Admin Division', 
         strain_date = 'Collection Data') %>% 
  
  # full_join to our tree to ensure no data is lost although you could also carefully use a left_join
  full_join(x=SC2_variants_time.tb, y=., by=c("label" = "strain")) %>% 
  
  # Convert to a tidytree format
  as.treedata()

# Look at the resulting structure
str(SC2_variants_time.tree)
Formal class 'treedata' [package "tidytree"] with 11 slots
  ..@ file       : chr(0) 
  ..@ treetext   : chr(0) 
  ..@ phylo      :List of 5
  .. ..$ edge       : int [1:12776, 1:2] 6628 6631 6632 6633 6636 6635 6638 6638 6639 6640 ...
  .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. ..$ : NULL
  .. .. .. ..$ : chr [1:2] "parent" "node"
  .. ..$ edge.length: num [1:12776] 0.0757 0 0.0077 0 0 ...
  .. ..$ tip.label  : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
  .. ..$ node.label : chr [1:6150] "NODE_0008606" "NODE_0000001" "NODE_0000061" "USA/MA1/2020_travel_history" ...
  .. ..$ Nnode      : int 6150
  .. ..- attr(*, "class")= chr "phylo"
  ..@ data       : tibble [12,777 x 8] (S3: tbl_df/tbl/data.frame)
  .. ..$ node               : int [1:12777] 1 2 3 4 5 6 7 8 9 10 ...
  .. ..$ GISAID             : chr [1:12777] "L" "L" "L" "L" ...
  .. ..$ emerging_nextstrain: chr [1:12777] "19A" "19A" "19A" "19A" ...
  .. ..$ PANGO              : chr [1:12777] "B" "B" "B" "B" ...
  .. ..$ strain_country     : chr [1:12777] "Asia" "USA" "Canada" "Canada" ...
  .. ..$ strain_division    : chr [1:12777] "Asia" "Massachusetts" "Ontario" "Ontario" ...
  .. ..$ strain_date        : Date[1:12777], format: "2019-12-26" "2020-01-29" ...
  .. ..$ Age                : num [1:12777] 44 21 NA NA 54 NA 47 47 23 55 ...
  ..@ extraInfo  : tibble [0 x 0] (S3: tbl_df/tbl/data.frame)
 Named list()
  ..@ tip_seq    : NULL
  ..@ anc_seq    : NULL
  ..@ seq_type   : chr(0) 
  ..@ tipseq_file: chr(0) 
  ..@ ancseq_file: chr(0) 
  ..@ info       : list()

1.3.1.1 How to access S4 object types

Take a quick look at the structure of this treedata object. What do we see? The treedata class has converted our tibble back into an S4 object with 11 slots. Each slot, is essentially a placeholder for another object. We can access these slots using the @ operator and we can further access sub elements with the $ operator.

For instance, we can see there is a phylo slot which looks suspiciously like our original SC2_variants_time.phylo object. The rest of our data frame information, which we just added to the tbl_tree before converting it is stored in the data slot. There are additional slots where we can add sequencing information for comparison in different types of phylogenetic tree visualizations.

# Grab our phylo object
...
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Take a peek at our associated node data
head(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context

1.4.0 Plot your tree with ggtree() from the ggtree package

Now that we’ve put some extra data into our tree, we are ready to plot it with the help of the ggtree() function from the ggtree package. If you haven’t noticed yet, treeio, tidytree and ggtree form a suite of packages that we can use to import, alter, and visualize our trees.

Parameters for ggtree() include:

  • tr: the phylo tree object

  • layout: the shape of the tree including: rectangular, dendrogram, slanted, ellipse, fan, circular, inward_circular, and radial.

  • mrsd: most recent sampling date used for setting a time-based scale

  • as.Date: logical to specify if date will be in calendar vs decimal-date format

  • branch.length: variable for scaling branch length

  • root.position: the position of our root node (default = 0)

You’ll notice that ggtree() appears to act very much like the ggplot() command. We’ll be able to add layers to the base visualization much like we do with other ggplot objects. We’ll also use theme_tree2() which will automatically add an x-axis scale to our tree.

We’ll start simple by just visualizing all 6,627 tips of our tree.

# Show the entire tree

SC2_variants_time.tree %>% 

# 1. Data
ggtree(tr = .) + 
  # 2. Aesthetics
  aes(color = ...) + 
  
  # Themes
  theme_tree2() + 
  theme(text = element_text(size = 20),
        legend.position = "bottom") + # Move our legend to the bottom

  # Provide some labels
  labs(x="Time",
       title = "Canada and US sequenced strain phylogeny") +

  # Make our guide lines a little thicker 
  guides(colour = guide_legend(title = "Nextstrain\nclade",  
                                        override.aes = list(size=2)))   
Error in ggtree(tr = .): could not find function "ggtree"

1.5.0 Filter your tree using drop.tip()

Looking at our visualization, there are a lot of tips to display and we’ve coloured the tree along the branches. Another thing we can see is that there is an “NA” value. This mostly represents the branches of the tree connecting nodes internally since they do not have a specific lineage associated from our data.

As it stands, there’s just way too much data here to look at and work with. We can trim that data using the drop.tip() function without worrying about how the tree is parsed. Internally, all that pruning will take place within the function. To do this, we’ll generate a list of tips we want to remove first.

# You can make a list of tips to drop when you're making your ggtree

# Generate a table of tree information to drop
to_drop <-

  SC2_metadata.df %>% 
  
  # Filter for strains that are not from Canada and are from before 2020-11-01
  filter(!(Country == ... &
           `Collection Data` >= ...
          )) %>%
  
  # Just keep the strain information
  pull(Strain)
Error in `filter()`:
i In argument: `!(Country == ... & `Collection Data` >= ...)`.
Caused by error:
! '...' used in an incorrect context
# Take a look at what we got back
str(to_drop)
Error in str(to_drop): object 'to_drop' not found
# At the same time make a dataset to keep. You'll use this to update the information in the tree later
to_keep <- SC2_metadata.df %>% filter(...)
Error in SC2_metadata.df %>% filter(...): '...' used in an incorrect context
# Which tips are we keeping?
str(to_keep, give.attr = FALSE)         
Error in str(to_keep, give.attr = FALSE): object 'to_keep' not found

1.6.0 Update our pruned tree and add some extra geom_* layers

The ggtree package brings a number of ggplot2-compatible geoms to our finger-tips. We’ll spruce up our tree with some tip labels to begin with. We’ll accomplish this with the geom_tiplab() layer.

Component type Layer type Command Description
Tree Geom geom_tree tree structure layer, with multiple layout supported
Tree Geom geom_treescale tree branch scale legend
Node Geom geom_nodepoint annotate internal nodes with symbolic points
Node Annotation geom_range bar layer to present uncertainty of evolutionary inference
Node Annotation geom_rootpoint annotate root node with symbolic point
Branch Annotation geom_label2 modified version of geom_label, with subsetting supported for labelling branches
Branch Annotation geom_segment2 modified version of geom_segment, with subsetting supported
Taxa Geom geom_point2 modified version of geom_point, with subsetting supported
Taxa Geom geom_tippoint annotate external nodes with symbolic points
Taxa Annotation geom_taxalink associate two related taxa by linking them with a curve
Taxa Annotation geom_text2 modified version of geom_text, with subsetting supported
Taxa Annotation geom_tiplab layer of tip labels
Taxa Annotation geom_tiplab2 layer of tip labels for circular layout
Clade Annotation geom_balance highlights the two direct descendant clades of an internal node
Clade Annotation geom_cladelabel annotate a clade with bar and text label
Clade Annotation geom_hilight highlight a clade with rectangle
Clade Annotation geom_strip annotate associated taxa with bar and (optional) text label
# Show our pruned tree

SC2_variants_time.tree %>%
  # drop tips based on our list
  drop.tip(., to_drop) %>% 

  # 1. Data
  ggtree(tr = .) + # Need the most recent sampling date
    # 2. Aesthetics
    aes(color = emerging_nextstrain) + 
    
    # Themes
    theme_tree2() + 
    theme(text = element_text(size = 20),
          legend.position = "bottom") + # Move our legend to the bottom
  
    # Provide some labels
    labs(x="Time",
         title = "Subset of Canadian sequenced strains") +
    
    # Make our guide lines a little thicker 
    guides(colour = guide_legend(title = "Nextstrain\nclade",  
                                          override.aes = list(size=2))) +    
    
    # 4. Geoms
    ### 1.6.0 Add tips labels to our tree tips
    ...    
Error in ggtree(tr = .): could not find function "ggtree"

1.6.1 Replace labels with points using different geoms

In a tree this packed, you can’t really read the tip labels. The data that’s most helpful, however, is the colouring. You can replace your labels with points in 3 ways:

  • geom_point()

  • geom_nodepoint()

  • geom_tippoint()

If we use geom_point() we can colour/annotate all of the nodes and tips but we already know that there isn’t any phylogenetic information associated with our internal nodes. Likewise, geom_nodepoint() will allow us to colour the nodes but there isn’t any grouping information associated with that.

Therefore we’ll use geom_tippoint() to colour our tips based on their Nextstrain clade information and we can alter the tip shape to match the province where the strain was sequenced.

# Show entire tree

SC2_variants_time.tree %>%
  drop.tip(., to_drop) %>% 

  # 1. Data
  ggtree(tr = .) + # Need the most recent sampling date
    # 2. Aesthetics
    aes(color = emerging_nextstrain) + 
    
    # Themes
    theme_tree2() + 
    theme(text = element_text(size = 20),
          legend.position = "bottom", # Move our legend to the bottom
          legend.box = "vertical") +  # Stack legends vertically
  
    # Provide some labels
    labs(x="Time",
         title = "Subset of Canadian sequenced strains") +
    
    # Make our guide lines a little thicker 
    guides(colour = guide_legend(title = "Nextstrain\nclade",  
                                          override.aes = list(size=2)),
           shape = guide_legend(title = "Strain\nlocation")
          ) + 
  
    # 3. Scaling
    scale_shape_manual(values = c(15:20, 11)) +
    
    # 4. Geoms
    # Add tips labels to our tree tips
    ### 1.6.1 Change our tip shape by strain_division
    ...  # Add tips only    
Error in ggtree(tr = .): could not find function "ggtree"

1.6.2 Fix your x-axis with the mrsd parameter

As you can see we are working with a time-based axis but it all appears to be on a relative scale and what we’d really like to see is real-world time. In order to do that, we can assign the mrsd parameter to the most recent sampling date in our dataset. We’ll also set our parameter as.Date in order to see the date in a calendar format rather than a decimal-date format.

# Show entire tree

SC2_variants_time.tree %>%
  drop.tip(., to_drop) %>% 
  
  # 1. Data
  ggtree(tr = ., 
       mrsd = ...,    ### 1.6.2 Set the most recent sampling date
       as.Date = ...) +       ### 1.6.2 Make sure it's set as a data 
    # 2. Aesthetics
    aes(color = emerging_nextstrain) + 
    
    # Themes
    theme_tree2() + 
    theme(text = element_text(size = 20),
          legend.position = "bottom", # Move our legend to the bottom
          legend.box = "vertical") +  # Stack legends vertically

    # Provide some labels
    labs(x="Time",
         title = "Subset of Canadian sequenced strains") +
    
    # Make our guide lines a little thicker 
    guides(colour = guide_legend(title = "Nextstrain\nclade",  
                                          override.aes = list(size=2)),
           shape = guide_legend(title = "Strain\nlocation")
          ) + 

    # 3. Scaling
    scale_shape_manual(values = c(15:20, 11)) +

    # 4. Geoms
    # Add tips labels to our tree tips
    # Change our tip shape by strain_division
    geom_tippoint(aes(shape = strain_division), alpha = 0.7, size = 3)  # Add tips only    
Error in ggtree(tr = ., mrsd = ..., as.Date = ...): could not find function "ggtree"

1.7.0 Thin out the tree some more with viewClade() and other information functions

Looking at the tree there are still quite a few nodes but we can zoom in on a specific clade using the viewClade() function. To accomplish this we need to access the most recent common ancestor (MRCA) of a group. At the same time, we should talk about ways to traverse or navigate this tree. Here’s where tidytree offers up a few additional functions for searching your tbl_tree. These take on the form of function(tbl_tree, node_number) or function(tbl_tree, node_label)

function Description
child() Find the children of an internal node.
parent() Find the parent node of a tip or other node.
offspring() Find all of the offspring of a node.
ancestor() Find all of the ancestors of a tip or node
sibling() Find the nearest sibling of a tip (or node)
MRCA() Find the most recent common ancestor between two or more nodes

Let’s filter our taxa list a little more searching for variants of the clade 501Y.V1 and 501Y.V2. Then we’ll find the MRCA between those tips.

# You can make a list of tips to drop when you're making your ggtree

# Generate a table of tree information to drop
to_view <-

  SC2_metadata.df %>% 
  
    # Filter our tip information
    filter(`Emerging Nextstrain clade` %in% c("20I/501Y.V1", "20H/501Y.V2") & 
           Country == ... &
           `Collection Data` >= as.Date("2021-02-16")
          ) %>% 
  
    # Just keep the strain names
    pull(Strain)
Error in `filter()`:
i In argument: `... & `Collection Data` >= as.Date("2021-02-16")`.
Caused by error:
! '...' used in an incorrect context
# What's the list look like?
to_view
Error in eval(expr, envir, enclos): object 'to_view' not found
# Calculate the MRCA from a subset of our tips

to_view_MRCA <-
  # Pass along our tree information
  SC2_variants_time.tree %>%
  
  # Get rid of the tips we would before (to match the tree we'll plot)
  drop.tip(., to_drop) %>%
  
  # Determine the MRCA between the first 4 tips in the list
  MRCA(., ...)
Error in SC2_variants_time.tree %>% drop.tip(., to_drop) %>% MRCA(., ...): '...' used in an incorrect context
# What kind of value does MRCA() return?
to_view_MRCA
Error in eval(expr, envir, enclos): object 'to_view_MRCA' not found
# How many offspring does it have?

# Pass along our tree information
SC2_variants_time.tree %>%
  
  # Drop our original set of tips
  drop.tip(., to_drop) %>% 
  # make a tibble so we can see all the relevant information
  as_tibble() %>% 
  # Find the offspring of a specific node
  offspring(., ...) %>% 
  # Convert to a tree
  as.treedata() %>% 
  str()
Error in as.treedata(.): '...' used in an incorrect context

1.7.1 Pass a ggplot2 object to viewClade()

We see that our chosen MRCA node (727) has 39 children based on the number of tip labels in our treedata object. We can also now use this MRCA to determine the specific clade we can see on the tree but this will still be quite large. To view this particular clade, we build our plot as before, and then zoom into it afterwards with the viewClade() function.

Unfortunately, we’ll have to ditch our date scale and revert to a decimal-date. If you’re not using a calendar-based date, it’s not a problem at all. If we look carefully at the data, we’ll see that the internal nodes have an NA-value. If you were industrious enough, you could use the branch lengths to traverse the tree and calculate dates for all the internal nodes as well. This would likely solve the issue.

Let’s drop the tip names back in there too.

# Show a small clade of the tree

tree.plot <-

SC2_variants_time.tree %>%
  # From our tree, drop the Non-Canadian tips
  drop.tip(., to_drop) %>% 
  
  # 1. Data
  ggtree(tr = ., mrsd = "2021-02-16") + # Need the most recent sampling date
    # 2. Aesthetics
    aes(color = emerging_nextstrain) + 
    
    # Themes
    theme_tree2() + 
    theme(text = element_text(size = 30),
          legend.position = "bottom", # Move our legend to the bottom
          legend.box = "vertical") +  # Stack legends vertically

    # Provide some labels
    labs(x="Time",
         title = "Subset of Canadian sequenced strains") +
    
    # Make our guide lines a little thicker 
    guides(colour = guide_legend(title = "Nextstrain\nclade",  
                                          override.aes = list(size=2)),
           shape = guide_legend(title = "Strain\nlocation")
          ) + 

    # 3. Scaling
    scale_shape_manual(values = c(15:20, 11)) +

    # 4. Geoms
    geom_tiplab(size = 7, align=TRUE) + # Add labels in and right-align them 

    # Add tips labels to our tree tips
    # Change our tip shape by strain_division
    geom_tippoint(aes(shape = strain_division), size = 3)  # Add tips only   
Error in ggtree(tr = ., mrsd = "2021-02-16"): could not find function "ggtree"
### 1.7.1 View the clade we made using the same MRCA call we already used.
viewClade(tree.plot, ...)
Error in viewClade(tree.plot, ...): could not find function "viewClade"

1.8.0 Make a new subplot with information from across all clades

So we’ve looked at a few ways to subset and plot our phylogenetic tree. We’ll do a quick aside to create a more curated list of tips with representation from across multiple clades. We’ll use this as the base tree for our next few graphs so that we can really see the power of using additional information from our treedata object.

# Here's our representative list of tips from multiple clades

curated_tips <-
c('Wuhan/WH01/2019',
'Canada/ON_ON-VIDO-01-2/2020',
'Canada/ON_VIDO-01/2020',
'Canada/BC_37_0-2/2020',
'Canada/BC_69243/2020',
'Canada/QC-CHUM-2008003956A/2020',
'Canada/NL-NML-1387/2020',
'Canada/MB-NML-808/2020',
'Canada/NB-NML-16967/2021',
'Canada/BC_6129127/2020',
'Canada/AB-97776/2020',
'Canada/AB-12948/2020',
'Canada/BC-BCCDC-3564/2020',
'Canada/ON-S67/2020',
'Canada/AB-65233/2020',
'Canada/MB-NML-1057/2020',
'Canada/ON-UHTC-0366/2020',
'Canada/NS-NML-16218/2021',
'Canada/NB-NML-16966/2021',
'Canada/NS_13/2020',
'Canada/Qc-CHUM-2019203856A/2020',
'Canada/NB-NML-3272/2020',
'Canada/ON-UHTC-0267/2020',
'Canada/MB-NML-1037/2020',
'Canada/NS-NML-5213/2020',
'Canada/NS-NML-5141/2021',
'Canada/NS-NML-5140/2021',
'Canada/NS-NML-16208/2021',
'Canada/BC-BCCDC-7012/2020',
'Canada/ON-LTRI-1372/2020',
'Canada/BC-BCCDC-9736/2021',
'Canada/ON-S2125/2021',
'Canada/NS-NML-5181/2020',
'Canada/ON-PPS-21012021_0269/2021',
'Canada/QC-L00314539/2020',
'Canada/NS-NML-16238/2021',
'Canada/NL-NML-16822/2021',
'Canada/QC-L00324589001/2021',
'Canada/NL-NML-17194/2021')
# Make a new list of tips to drop
curated_drop_list <-
  SC2_metadata.df %>% 

  # filter the opposite of our tip labels
  filter(!(Strain %in% ...)) %>% 
  # Only keep the strain information for what we want to drop
  pull(Strain)
Error in `filter()`:
i In argument: `!(Strain %in% ...)`.
Caused by error:
! '...' used in an incorrect context
# Drop tips from our tree and save it as curated_tree_info.df
curated_tree_info.df <-

  # Pass along our original tree
  SC2_variants_time.tree %>%

  # Drop the tips we created
  drop.tip(., ...) %>% 

  # Convert to a tibble
  as_tibble() %>% 

  # Rename our column information
  rename(Clade = ..., Province = ...) %>% 

  mutate(Age = replace_na(Age, replace = 0)) %>% 

  # Convert this to a data frame
  as.data.frame()
Error in mutate(., Age = replace_na(Age, replace = 0)): '...' used in an incorrect context
# Set the rownames from the label information
rownames(curated_tree_info.df) <- curated_tree_info.df$label
Error in eval(expr, envir, enclos): object 'curated_tree_info.df' not found
# Let's view the tree
str(curated_tree_info.df)
Error in str(curated_tree_info.df): object 'curated_tree_info.df' not found

1.9.0 Convert your tree style with the layout parameter

So we’ve only been working with a rectangular tree (horizontal layout) thus far but as we mentioned at the beginning there are actually a number of different layouts we can use. Our goal now is to make a sort of sunburst plot using a circular layout that we’ll add visual categorical information to in a ring pattern.

First, we’ll start with the circular version. We’ll colour our tips by the province where the case or data was generated and label them by the Nextstrain clade information.

# Show our curated tree in a circular format

tree.plot <-

  SC2_variants_time.tree %>%
  drop.tip(., curated_drop_list)  %>% 
  
  # 1. Data
  ggtree(., layout = ..., ### 1.9.0 Set the layout to circular
         mrsd = "2021-02-17") + # Need the most recent sampling date
    # 2. Aesthetics

    # Themes
    theme(text = element_text(size = 20),
          legend.position = "bottom") +
    labs(title = "Canada and US sequenced strain phylogeny",
         colour = "Province") +

    scale_colour_viridis_d(option = "plasma") + # This seems to literally kill the kernel!

    # 4. Geoms
    geom_tippoint(aes(colour = strain_division), size = 5) + 

    ### 1.9.0 set the tip labels
    geom_tiplab(aes(label = ...), size = 8)
Error in ggtree(., layout = ..., mrsd = "2021-02-17"): could not find function "ggtree"
# View our tree
tree.plot
Error in eval(expr, envir, enclos): object 'tree.plot' not found

1.10.0 Use gheatmap() layer to add information to your plot

Now that we have our basic circular tree, we can use the information from curated_tree_info.df to visualize additional data on our tree. In order to use the gheatmap layer properly, we must pass it our original tree plot, along with a new dataframe (or vector) that holds the information we want to add. It should use the rownames from the data frame to help match up to the tips in our tree.

We’ll drop the tip labels from our tree in favour of the heatmap we’ll add. By adding layers of hierarchical data, this creates what is known as a Sunburst plot.

The gheatmap() layer takes some of the following parameters:

  • p: the tree plot we want to modify.

  • data: the data used to modify the tree plot p.

  • offset: determines the offset of the heatmap relative to the tree.

  • width: determines the total width of the heatmaps compared to the tree

  • colnames_angle: determines the angle of the text for the heatmap.

  • font.size: sets the font size for the heatmap portion of the tree.

# Show our curated tree in a circular format

tree.plot <-

  SC2_variants_time.tree %>%
  drop.tip(., curated_drop_list)  %>% 

  # 1. Data
  ggtree(., layout = "circular", mrsd = "2021-02-17") + # Need the most recent sampling date
    # 2. Aesthetics
    
    # Themes
    theme(text = element_text(size = 20),
          legend.position = "bottom") +
    labs(title = "Canada and US sequenced strain phylogeny",
         colour = "Province") +

    scale_colour_viridis_d(option = "plasma") +

    # 4. Geoms
    geom_tippoint(aes(colour = strain_division), size = 5) # Add tips only 
Error in ggtree(., layout = "circular", mrsd = "2021-02-17"): could not find function "ggtree"
### 1.10.0 Add a circular heatmap with gheatmap()
tree.plot <- ...(tree.plot, ..., 
                      offset = 0.1, width = 0.1,
                      colnames_angle = 90, font.size = 6, hjust = 1) + 
  scale_fill_continuous(name = "Tree coding") 
Error in ...(tree.plot, ..., offset = 0.1, width = 0.1, colnames_angle = 90, : could not find function "..."
tree.plot
Error in eval(expr, envir, enclos): object 'tree.plot' not found

1.10.1 ggnewscale package allows multiple color/fill scales on your Sunburst plot

From our sunburst plot above we used multiple columns from curated_tree_info.df but the caveat is that the legend for each resulting column combined all of the data into a single new legend. That’s helpful if the type of data could be continuous values like a [0,1] scale heatmap colour across different features. When dealing with multiple categories where the scale of the values varies, however, that doesn’t work well for us. This also doesn’t work well for categorical data.

Instead, we’d like to separate the colour/fill scales by repeatedly adding to our plot, layer by layer. As you might recall, however, we also run into the problem of scale_colour_* and scale_fill_* layers overwriting any previous layers.

To circumvent this reality, we’ll use the ggnewscale package to generate new colour and fill scales with new_scale_fill() and new_scale_colour(). It can make the process slightly more encumbering but it will work out.

We’ll add back in our original tip labels as well for this graph.

# Show our curated tree in a circular format

# We'll need some extra colour sets to accomplish this plot
combo.colours = c(brewer.pal(12, "Paired"), brewer.pal(8, "Set1"), brewer.pal(12, "Set3"))


# Save our first plot with just the tree
tree.plot <-

  SC2_variants_time.tree %>%
  drop.tip(., curated_drop_list)  %>% 
  
  # 1. Data
  ggtree(., layout = "circular", mrsd = "2021-02-17") + # Need the most recent sampling date
    # 2. Aesthetics
    
    # Themes
    theme(text = element_text(size = 30),
          legend.position = "bottom") +
    labs(title = "Canada and US sequenced strain phylogeny") +

    # We'll want to ensure our colour guide ends up in the correct order
    scale_colour_discrete(guide = guide_legend(title="Province", order=1)) +

    # 4. Geoms
    geom_tippoint(aes(colour = strain_division), size = 5) + # Add tips only 
    geom_tiplab(size = 10, align=TRUE, offset = 0.6) # Add in our labels
Error in ggtree(., layout = "circular", mrsd = "2021-02-17"): could not find function "ggtree"
# Generate our first heatmap layer
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(parent, node, branch.length), 
                      offset = 0.1, width = 0.1,
                      colnames_angle = 90, font.size = 6) + 
  # Set the name of our legend
  scale_fill_continuous(name = "Tree coding",
                        guide = guide_legend(order = 2)) 
Error in gheatmap(tree.plot, curated_tree_info.df %>% select(parent, node, : could not find function "gheatmap"
### 1.10.1 use this code to create a new colour scale
tree.plot <- tree.plot + ... 
Error in eval(expr, envir, enclos): object 'tree.plot' not found
# Generate a categorical heatmap layer for the Clade variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(Clade), 
                      offset = 0.2, width = 0.1,
                      colnames_angle = 90, font.size = 12) + 
    
  # Set the name and order of our Clade legend
  scale_fill_discrete(name = "Nextstrain\nClade", 
                      guide = guide_legend(order=3))
Error in gheatmap(tree.plot, curated_tree_info.df %>% select(Clade), offset = 0.2, : could not find function "gheatmap"
### 1.10.1 use this code to create a new colour scale
tree.plot <- tree.plot + ... 
Error in eval(expr, envir, enclos): object 'tree.plot' not found
# Generate a categorical heatmap layer for the PANGO variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(...), 
                      offset = 0.35, width = 0.1,
                      colnames_angle = 90, font.size = 12) +     

  # Set the name and order of our PANGO legend
  scale_fill_manual(values = combo.colours, name = "PANGO\nlineage", 
                    guide=guide_legend(order=4))
Error in gheatmap(tree.plot, curated_tree_info.df %>% select(...), offset = 0.35, : could not find function "gheatmap"
# You may need to re-render to see it properly (Expand/collapse again)
tree.plot
Error in eval(expr, envir, enclos): object 'tree.plot' not found

1.10.2 Create a vertical version of our plot using a rectangular layout

Depending on the level of data depth, you may also choose to layer your scales along the right-hand side of your tree. To accomplish this we can simply set our layout parameter to “rectangular”. At the same time, we’ll run into a couple of problems with how the ggtree is made.

One issue with ggplot is that as we add the scales, these are very much like annotations on our ggplot. The vertical (and horizontal) display areas of our plot are not updated as we add these annotations. When we add new colour scale layers to the plot, the labels will, in many cases, bleed outside of the designated plot area. To combat this, we can add a layer called vexpand() or hexpand() for horizontal expansion. Both of these take 2 parameters:

  • ratio: the ratio of expansion for your plot axis
  • direction: the direction you want the expansion in a range from 1 (left/top) to -1 (right/bottom).
# Show our curated tree in a rectangular format

# We'll need some extra colour sets to accomplish this plot
combo.colours = c(brewer.pal(12, "Paired"), brewer.pal(8, "Set1"), brewer.pal(12, "Set3"))

# Save our first plot with just the tree
tree.plot <-

  SC2_variants_time.tree %>%
  drop.tip(., curated_drop_list)  %>% 
  
  # 1. Data
  ggtree(., layout = ..., ### 1.10.2 convert your plot to a rectangular format 
         mrsd = "2021-02-17") + # Need the most recent sampling date
    # 2. Aesthetics
    
    # Themes
    theme(text = element_text(size = 30),
          legend.position = "bottom",
         ) +
    labs(title = "Canada and US sequenced strain phylogeny",
         colour = "Province") +

    ### 1.10.2 Expand the vertical plot size
    vexpand(ratio = ..., direction = ...) + 

    # We'll want to ensure our colour guide ends up in the correct order
    scale_colour_discrete(guide = guide_legend(title="Province", order=1)) +

    # 4. Geoms
    geom_tippoint(aes(colour = strain_division), size = 5) + # Add tips only 
    geom_tiplab(size = 10, align=TRUE) # Add in our labels 
Error in ggtree(., layout = ..., mrsd = "2021-02-17"): could not find function "ggtree"
# Generate our first heatmap layer
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(parent, node, branch.length), 
                      offset = 0.75, width = 0.1,
                      colnames_angle = 90, font.size = 8, hjust = 1) + 
  # Set the name of our legend
  scale_fill_continuous(name = "Tree coding",
                        guide = guide_legend(order = 2)) 
Error in gheatmap(tree.plot, curated_tree_info.df %>% select(parent, node, : could not find function "gheatmap"
# use this code to create a new colour scale
tree.plot <- tree.plot + new_scale_fill() 
Error in eval(expr, envir, enclos): object 'tree.plot' not found
# Generate a categorical heatmap layer for the Clade variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(Clade), 
                      offset = 0.85, width = 0.1,
                      colnames_angle = 90, font.size = 12, hjust = 1) + 
    
  # Set the name and order of our Clade legend
  scale_fill_discrete(name = "Nextstrain\nClade", 
                      guide = guide_legend(order=3))
Error in gheatmap(tree.plot, curated_tree_info.df %>% select(Clade), offset = 0.85, : could not find function "gheatmap"
# use this code to create a new colour scale
tree.plot <- tree.plot + new_scale_fill() 
Error in eval(expr, envir, enclos): object 'tree.plot' not found
# Generate a categorical heatmap layer for the PANGO variable
tree.plot <- gheatmap(tree.plot, curated_tree_info.df %>% select(PANGO), 
                      offset = 1.0, width = 0.1,
                      colnames_angle = 90, font.size = 12, hjust = 1) +     

  # Set the name and order of our PANGO legend
  scale_fill_manual(values = combo.colours, name = "PANGO\nlineage", 
                    guide=guide_legend(order=4))
Error in gheatmap(tree.plot, curated_tree_info.df %>% select(PANGO), offset = 1, : could not find function "gheatmap"
# View the tree
tree.plot
Error in eval(expr, envir, enclos): object 'tree.plot' not found

1.12.0 There is definitely more to explore from the ggtree package

So we’ve spent quite a bit of time looking at phylogenetic trees and how to add external data to tips and nodes. We’ve barely scratched the surface and there are a lot of additional functions within the ggtree package that you can work with to build amazing plots including adding fasta sequence data through msaplot(). You can experiment with labeling internal nodes, and specific clades as well. You can also facet_plot() rectangular trees with other kinds of plots!

We didn’t even have time to combine all sorts of plot data with our circular trees using the ggtreeExtra package. Definitely check out Chapter 10 of the tree book to be inspired by the potential things you could do with more complex datasets like this:

The geom_fruit() layer can be used to add extra visualization to your sunburst plot through the ggtreeExtra package


2.0.0 What are network diagrams and how can we use them?

We’ve seen a lot of trees today but a closely related structure is the network diagram. They share similar concepts in that both have nodes and branches. However, nodes are called vertices and can represent almost anything, branches between nodes are edges and can span across multiple nodes, instead of in a bifurcating tree relationship. Relationships between vertices can be bidirectional, and depending on what you’d like to present, multiple parallel edges may exist. We saw a specific version of network diagrams in Lecture 04 with our Sankey plots!

Network graphs are great for showing the interconnections between entities within your data. This kind of visualization can also help us realize where subtle connections occur (or don’t occur!). Depending on how you’ve weighted or chosen edges, you can convey how strong relationships between entities are as well.

To work with graph data and plot it, we’ll be using a couple of companion packages: tidygraph and ggraph. More about that later!

Since we already have some metadata about COVID genomes, let’s see if we can’t convert some of it to a network diagram to better understand strain information? We’ll begin by selecting some information from our Nextstrain metadata set.

str(SC2_metadata.df, give.attr = FALSE)
spc_tbl_ [6,627 x 24] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Strain                    : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
 $ GISAID clade              : chr [1:6627] "L" "L" "L" "L" ...
 $ Nextstrain clade          : chr [1:6627] "19A" "19A" "19A" "19A" ...
 $ Age                       : num [1:6627] 44 21 NA NA 54 NA 47 47 23 55 ...
 $ Clade                     : chr [1:6627] "19A" "19A" "19A" "19A" ...
 $ Country                   : chr [1:6627] "Asia" "USA" "Canada" "Canada" ...
 $ Admin Division            : chr [1:6627] "Asia" "Massachusetts" "Ontario" "Ontario" ...
 $ Admin Division of exposure: chr [1:6627] "Asia" "Asia" "Ontario" "Asia" ...
 $ gisaid_epi_isl            : chr [1:6627] "EPI_ISL_406798" "EPI_ISL_409067" "EPI_ISL_425177" "EPI_ISL_413015" ...
 $ Host                      : chr [1:6627] "Human" "Human" "Human" "Human" ...
 $ Originating Lab           : chr [1:6627] "General Hospital of Central Theater Command of People's Liberation Army of China" "Massachusetts Department of Public Health" "Public Health Ontario" "Public Health Ontario Laboratory" ...
 $ PANGO lineage             : chr [1:6627] "B" "B" "B" "B" ...
 $ Submission Date           : chr [1:6627] "Older" "Older" "Older" "Older" ...
 $ Region                    : chr [1:6627] "Asia" "North America" "North America" "North America" ...
 $ Region of exposure        : chr [1:6627] "Asia" "Asia" "North America" "Asia" ...
 $ Sex                       : chr [1:6627] "Male" "Male" "Male" "Male" ...
 $ strain                    : chr [1:6627] "Wuhan/WH01/2019" "USA/MA1/2020" "Canada/ON_ON-VIDO-01-2/2020" "Canada/ON_VIDO-01/2020" ...
 $ Emerging Nextstrain clade : chr [1:6627] "19A" "19A" "19A" "19A" ...
 $ Submitting Lab            : chr [1:6627] "BGI & Institute of Microbiology, Chinese Academy of Sciences & Shandong First Medical University & Shandong Aca"| __truncated__ "Pathogen Discovery, Respiratory Viruses Branch, Division of Viral Diseases, Centers for Disease Control and Prevention" "Public Health Agency of Canada - National Microbiology Laboratory" "National Microbiology Laboratory" ...
 $ url                       : logi [1:6627] NA NA NA NA NA NA ...
 $ Collection Data           : Date[1:6627], format: "2019-12-26" "2020-01-29" ...
 $ Author                    : chr [1:6627] "Weijun Chen et al (https://dx.doi.org/10.1016/S0140-6736(20)30251-8)" "Clinton R. Paden et al (https://dx.doi.org/10.1101/2020.03.09.20032896)" "Amrit S. Boese et al" "Shari Tyson et al" ...
 $ Country of exposure       : chr [1:6627] NA "Asia" NA "Asia" ...
 $ Location                  : chr [1:6627] NA "Boston" NA NA ...

2.0.1 Perform a bit of data wrangling to select our data

Before we jump into making some network diagrams, we’ll want to trim down the data from SC2_metadata.df. We don’t need all of the data that is currently in this table. Instead we’ll trim it down to 10 variables:

  • Strain: the strain name of the genome. This matches the tips of our original trees we made in section 1.0.0.
  • Country: the country where the case was reported.
  • Admin Division: the sub-region where the case was identified
  • Admin Division of exposure: the sub-region believed to be the source of the strain.
  • Region: appears to be the continent where the case was identified.
  • Region of exposure: the continent was the case was contracted.
  • Nextstrain clade: the Nextstrain clade for this strain
  • GISAID clade: the clade classifiction defined by the Global Initiative on Sharing Avian Influenza Data
  • PANGO lineage: the Phylogenetic Assignment of Named Global Outbreak lineage
  • Collection Data: the collection date of the strain (also misspelled)
SC2_graph_info.df <-

# Pass along our metadata
SC2_metadata.df %>% 

  # We'll grab information about each case that relates to its geographical region
  ...(Strain, Country, 'Admin Division', 'Admin Division of exposure', 'Region of exposure', Region,
         'Nextstrain clade', 'GISAID clade', 'PANGO lineage', 'Collection Data') %>% 
  
  # Do a bunch of renaming for our variables
  ...(source_location = 'Admin Division of exposure', 
         case_location = 'Admin Division',
         source_region = 'Region of exposure',
         case_region = 'Region',
         collection_date = 'Collection Data',
         Nextstrain = 'Nextstrain clade',
         GISAID = 'GISAID clade',
         PANGO = 'PANGO lineage'
        )
Error in ...(., source_location = "Admin Division of exposure", case_location = "Admin Division", : could not find function "..."
head(SC2_graph_info.df)
Error in head(SC2_graph_info.df): object 'SC2_graph_info.df' not found

2.1.0 Use the tidygraph package to help prepare data

Now that we have some information that we want to work with, we want to convert that kind of data to something that can be interpreted into a graph. The tidygraph package provides a way to hook graph data into the tidyverse so that we can use common verbs and ideas to filter and work with it. Using this package we can convert our dataframe information into a tbl_graph object which is actually an igraph object.

The function we’ll use to convert our data is as_tbl_graph() but it has some expectations about the data. The parameters of this function are:

  • x: the data frame we’d like to convert to an igraph.

  • nodes: a data frame with our node information.

  • edges: a data frame of two columns containing integers matching node information to describe the relationship between nodes.

If you have both a nodes and edges data frame you can certainly generate a table this way. However, we have a complex dataframe and we want to track all of the information in it. To that end we will add columns from and to based on variables that already exist and the graph nodes will be generated from this information. When we provide the data frame, the function will recognize the columns present and produce node-based data and generate edge characteristics from our other columns.

# Now we'll add some specific variables used to generate our graph table

SC2_graph_info.df %>% 

  # Create our "from" and "to" columns in our data frame
  mutate(from = source_location,
         to = Country
        ) %>%  

  # Filter for just strain data from Canada and Asia
  filter(Country %in% c(...)) %>% 

  # Convert to an igraph
  as_tbl_graph() %>% 

  # Look at the structure of the igraph
  str()
Error in as_tbl_graph(.): could not find function "as_tbl_graph"

2.2.0 Plot your graph information with ggraph()

The ggraph() function is one of many from a package of the same name. This package adds geoms and architecture that is compatible with ggplot2 (for the most part). Some of the functions we are interested in are:

Component Function Description
Graph ggraph The equivalent of the call to ggplot, it sets up the basic information about the graph plot
Edge geom_edge_link Produces edges between points
Edge geom_edge_fan Produces edges between points but accounts for parallel edges, creating arcs that fan out
Edge geom_edge_parallel Produces multiple edges between points with parallel lines representing parallel edges
Edge geom_edge_loop Used to represent edges that start and end at the same node. Does not account for parallel edges
Node geom_node_point Add basic nodes to your graph
Node geom_node_circle Add nodes to your graph that can be scaled by the coordinate system

The ggraph() function takes in 3 parameters:

  • graph: the igraph object that we’ll base the graph on.

  • layout: the type of layout for the graph. There are many options including: auto, dendrogram, linear, matrix, treemap (yep!) circlepack, partition, and hive.

  • circular: a logical stating whether the layout should be transformed into a radial representation. It can’t be applied to all layouts (think polar_coord).

# Now we'll add some specific variables used to generate our graph table

SC2_graph_info.df %>% 

  # Generate our from and to edge information
  mutate(from = source_region,
        to = case_region
        ) %>%  

  # Convert to an igraph and pass it on to be plotted
  as_tbl_graph() %>% 

  # 1. Data
  ggraph(.) + 
      # 2. Aesthetics
      theme(text = element_text(size = 10)) +

      ### 2.2.0 Add our edges
      ...(aes(colour = Nextstrain), arrow=arrow(), width = 1) +

      ### 2.2.0 Add our nodes and label them
      ...(size = 7) + 
      ...(aes(label = name), size = 5, repel = TRUE)
Error in ggraph(.): could not find function "ggraph"

2.2.1 Use geom_edge_fan() to visualize parallel edges

So from our graph we can see some characteristics about how our case data is connected. Although most of the data is centred around genomes sequenced in North America, we can see that “North America” is also a source region.

The way we see the edges, however also has all of them overlaying on top of each other. We know that this isn’t going to be a 1:1 relationship and although we have coloured the edges, we only see the topmost edge in a stack of many. For a graph like this, if we want to see all of the edges, we can use the geom_edge_fan() layer which will help us see all of our parallel edges in all of their splendour. Of note, we can also use geom_edge_parallel() as a layer. While this layer may make the number of connections clearer, it can get quite crowded.

Let’s try it out.

# Now we'll add some specific variables used to generate our graph table

SC2_graph_info.df %>% 

  # Generate our from and to edge information
  mutate(from = source_region,
        to = case_region
        ) %>%  

  # Convert to an igraph
  as_tbl_graph() %>% 

  # 1. Data

  ggraph(.) + 
      # 2. Aesthetics
      theme(text = element_text(size = 10)) +

      ### 2.2.1 Add our edges
      ...(aes(colour = ...), arrow=arrow()) +

      # Add our nodes and label them
      geom_node_point(size = 7) + 
      geom_node_text(aes(label = name), size = 5, repel = TRUE)
Error in ggraph(.): could not find function "ggraph"

2.2.2 Filter and choose your data carefully to better visualize it

The metadata we’ve chosen isn’t very detailed and that can be the case for many datasets so you’ll want to be sure of how you pick your nodes. Right now we are picking our from and to values based on the supposed source and identifying continents of the strain.

While other variables could offer more information, they can vary in consistency from a region (Asia) to a province (Ontario) as seen in variable case_location. Definitely aim to be consistent when you’re working with your data otherwise the nodes may have less meaning.

Let’s try looking at source_region (Continent) to case_location (Divisions) as it relates to data from Canada and Asia

# Now we'll add some specific variables used to generate our graph table

SC2_graph_info.df %>% 

  # Generate our from and to edge information
  mutate(from = ...,
        to = ...
        ) %>%  

  # Filter for Canada data
  filter(Country %in% c("Canada", "Asia")) %>% 

  # Convert to an igraph
  as_tbl_graph() %>% 

  # 1. Data
  ggraph(.) + 
    # 2. Aesthetics
    theme(text = element_text(size = 20)) +

    # Add our edges
    geom_edge_fan(aes(colour = Nextstrain), arrow=arrow()) +
    ### 2.2.2 If we suspect looping edges, we need to define that layer specifically
    ... +

    # Add our nodes and label them
    geom_node_point(size = 7) + 
    geom_node_text(aes(label = name), size = 7, repel = TRUE)
Error in ggraph(.): could not find function "ggraph"

2.3.0 There are many more visualizations from ggraph

Interestingly in the first year of the pandemic, from the sequenced genome data, infections within Canada appear to originate mostly from within North America but there are some infections that originated from Asia, and entered through Ontario and British Columbia - our two main global ports of entry!

We’ve really only covered linear graph layouts in our data but there are actually many kinds of graphs that this package can produce. The layout parameter is the key to exploring all of the graph types available. Of course your data layout all has to make sense! You’ll find great examples from data-imaginist.com like this circlepack graph:

There are all kinds of network graphs that can use additional layers of metadata to shape and present your data


3.0.0 Sequence motifs and genomic markers

When working with data sometimes you may be working with sequence-specific data for analysis of motifs or you may have a large set of data describing genomic markers like single nucleotide variants. Two popular visualizations in this domain are the sequence logo and Manhattan plot.

3.2.0 The Manhattan plot for all your genome-position visualization needs

The Manhattan plot is usually used in visualizing data across the genome from LOD scores to marker proportions.

Named for it’s visual similarity to the Manhattan skyline of singular towering skyscrapers scattered above low-level buildings, this plot is commonly used for visualizing genomic markers across an ordered linear axis like a series of chromosomes. The y-axis of the values can represent LOD scores or proportions of marker representation. This is frequently used to visualize GWAS or mapping data.

This is another scatterplot that we can generate directly in ggplot with some effort/setup or we can use a package like qqman.

First let’s look at the kind of data. Fun fact! You can read in archived/zipped data as well, although be careful, GWAS files can be quite large!

load(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
str(..., give.attr = FALSE)
Error in eval(expr, envir, enclos): '...' used in an incorrect context

3.2.1 Use the manhattan() function to generate a plot

To generate our Manhattan plot we can use the manhattan() wrapper function which will require four sets of parameter information:

  • chr: the column name with the chromosome information for a given SNP.

  • bp: the basepair position of the SNP.

  • snp: the ID of the SNP usually a RefSNP ID (RSID) of some kind.

  • p: the p-value for that particular SNP generated from the case vs. control analysis. This will be converted to a \(-log10(p)\) value for the y-axis.

# manhattan(gwasResults, chr="CHR", bp="BP", snp="SNP", p="P")

GWAS_data.df %>% 
# You can filter your data if you want this step to run faster
# filter(CHR %in% c(1:5)) %>%  

  ...(., chr="CHR", bp="POS", snp="rsid", p="p.value")
Error in ...(., chr = "CHR", bp = "POS", snp = "rsid", p = "p.value"): could not find function "..."

3.2.2 Use the highlight parameter to identify markers of interest

Now that we’ve plotted our Manhattan plot, we can see some horizontal lines corresponding to minimum p-values of 1.0 x 10-5 and 5.0 x 10-8 although the significance of these can depend on sample size and allele frequencies of your SNPs.

Either way, you can highlight SNP results based on a provided list. In this case, we can generate a list ourselves by filtering on the p.value variable.

snp_candidates <- 

  # Pass along our GWAS data
  GWAS_data.df %>% 

  # Filter it for low p-values
  filter(-log10(p.value) >= 5) %>% 

  # Select SNP candidates
  pull(...) %>% 
  unique()
Error in unique(.): '...' used in an incorrect context
# snp_candidates

# Plot our Manhattan plot
manhattan(GWAS_data.df, 
          chr="CHR", 
          bp="POS", 
          snp="rsid", 
          p="p.value", 
          highlight = ...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context

4.0.0 Tips and Tricks

When preparing figures for a presentation or manuscript consider some of the following tips/tricks that I’ve accumulated over the years. Not all may apply to your work but these can certainly be helpful.

4.1.0 How many figures do you need to tell your story?

Regardless of whether or not you’ll be giving a talk or submitting a manuscript you’ll want to consider how many figures you need to tell your story.

  • Most major publications will have 4-6 main figures with usually an (unlimited nowadays) number of supplemental figures.

    • Main figures can usually accomodate up to 4-8 subpanels depending on the complexity of your data/subject
  • Presentations generally have 1 slide per minute of talk so plan accordingly.

    • A complicated figure or figure type should be given some extra time to initially explain but if you are using the format often, later versions will be simpler to digest. Spend time discussing the first occurrence of a figure type to build a solid foundation!

4.2.0 Manuscript specifics

While publication requirements vary from journal to journal, most have similar rules

  • Minimum dpi (resolution) of your images: 300dpi

  • Minimum font size (ie the very smallest any font should be on a sub panel): Usually 6-7pts

  • Maximum image size. There is usually a maximum figure size in terms of dimensions but this really can vary widely. Check with a few potential publications first to get an idea of the main size/layout (portrait vs landscape) for figures.

  • Keep ALL figure data organized for submission! This is the standard now and can include sequences, or any data that was used to create your figures. You can organize this in a single CSV, or worksheet-based file.

  • Figures should be decipherable at a glance. A successful figure’s general message can be understood without having to read through the figure legends. This is not always possible but is certainly something to strive for.

    • Figure titles are optional. Often helpful for the above point but you sacrifice space to have them!

4.3.0 Presentation figures

  • Make your font big enough to read. Unlike a manuscript, people cannot zoom in on your presentation figures

  • Limit panels to 2-4 at most per figure.

  • Forget about the title on your figure. Title the slide instead with your take-home message about the figure. Having an effective slide title tells your audience where to focus on your figure. This is especially key when you are presenting a complicated or “busy” figure.

  • When presenting a figure with multiple categories or ideas, reveal it piecemeal. Present the base idea (ie a control) and then reveal the next part of the panel showing your first experimental condition, then your second etc. This allows the audience to acclimate to the idea you wish to convey.

  • If you can’t read the text of a figure because there are just too many things, consider simplifying or removing the text. This will reduce on distracting imagery and focus your message to your audience.

4.4.0 Things to think about when coding

  • Keep a script (or notebook!) for remaking all your figures!

    • Simplify with a few variables things like the location of your datasets!

    • Build functions to make figures of the same type with different datasets.

    • Create a master file/table to import and use these datasets and functions.

  • Create a specific colour-scheme for your figures

    • Try to use the same colour for controls versus specific mutant genotypes you may be examining or re-using frequently

    • Use a colourblind-friendly palette

  • Save your plots as SVG, PNG or TIFFs

    • SVGs are vectorized meaning they are files representing lines, curves, and shapes based on a coordinate system. This means they are generally resolutionless and can be blown up to any size as needed. Great for working with on presentations and simple to alter in most image editing programs. Ideal for using on graphs and ggplot figures but not microscopy or other digital images.

    • Avoid JPEGs as much as possible. The tend to have poor (lossy) encoding that can create resolution loss unless you save them as big high-quality JPEG versions (lossless).


5.0.0 Class Summary

With this final lecture we’ve covered the spectrum of visualizations covering the most basic of scatter- and barplots, graduating to boxplots, violin plots, and their variants. We’ve covered high-throughput data visualizations including volcano plots, heatmaps, and principal component analysis. Furthermore we looked at how simple the projection of high-dimension data can be with t-SNE and UMAP.

We finished our course today covering phylogenetic trees, network graphs, and some sequence analysis visualizations. Overall you now have the tools to wrangle data that may appear in all sorts of formats along with a better understanding of when and how to prepare some of the most common data visualizations in your burgeoning science careers.

Congratulations and good luck on your data science journey!


5.0.1 Post-course survey

We have created a post-course survey you can fill out anonymously. You can use this survey as an opportunity to tell us about your experience and help shape the future offerings of this series. Please take 5-10 minutes to fill out the survey. We really appreciate your feedback!

Anonymous Google Survey found here


5.1.0 Weekly assignment

This week’s assignment will be found under the current lecture folder under the “assignment” subfolder. It will include an R markdown notebook that you will use to produce the code and answers for this week’s assignment. Please provide answers in markdown or code cells that immediately follow each question section.

Assignment breakdown
Code 50% - Does it follow best practices?
- Does it make good use of available packages?
- Was data prepared properly
Answers and Output 50% - Is output based on the correct dataset?
- Are groupings appropriate
- Are correct titles/axes/legends correct?
- Is interpretation of the graphs correct?

Since coding styles and solutions can differ, students are encouraged to use best practices. Assignments may be rewarded for well-coded or elegant solutions.

You can save and download the markdown notebook in its native format. Submit this file to the the appropriate assignment section by 12:59 pm on the date of our next class: April 25th, 2024.


5.2.0 Acknowledgements

Revision 1.0.0: created and prepared for CSB1021H S LEC0141, 03-2021 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 1.0.1: edited and prepared for CSB1020H S LEC0141, 03-2022 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 1.0.2: edited and prepared for CSB1020H S LEC0141, 03-2023 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 2.0.0: Revised and prepared for CSB1020H S LEC0141, 03-2024 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.


5.3.0 References

The book of ggtree from the Yu Lab: https://yulab-smu.top/treedata-book/index.html

Chapter 10 of ggtree with amazing and complicated plots: https://yulab-smu.top/treedata-book/chapter10.html

More information on the ggraph package: https://cran.r-project.org/web/packages/ggraph/ggraph.pdf

ggseqlogo package information: https://cran.r-project.org/web/packages/ggseqlogo/ggseqlogo.pdf

Manhattan plot tutorial: https://www.r-graph-gallery.com/101_Manhattan_plot.html

GWAS p-value threshold: https://www.nature.com/articles/s41380-020-0670-3?proof=t

More on GWAS thresholds for significance: https://www.nature.com/articles/ejhg2015269.pdf


The Center for the Analysis of Genome Evolution and Function (CAGEF)

The Centre for the Analysis of Genome Evolution and Function (CAGEF) at the University of Toronto offers comprehensive experimental design, research, and analysis services in microbiome and metagenomic studies, genomics, proteomics, and bioinformatics.

From targeted DNA amplicon sequencing to transcriptomes, whole genomes, and metagenomes, from protein identification to post-translational modification, CAGEF has the tools and knowledge to support your research. Our state-of-the-art facility and experienced research staff provide a broad range of services, including both standard analyses and techniques developed by our team. In particular, we have special expertise in microbial, plant, and environmental systems.

For more information about us and the services we offer, please visit https://www.cagef.utoronto.ca/.

LS0tDQp0aXRsZTogJycNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgVGhpcyBhbGxvd3MgdGhlIGZpbGUgdG8gYmUgTElWRSBhbmQgcnVuIHdpdGhvdXQgZXJyb3JzIHN0b3BwaW5nIGl0Lg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSkNCmBgYA0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX3NlcnZpY2VzX3NsaWRlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQoNCiMgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSIHRlc3QNCg0KIyBMZWN0dXJlIDA2OiBUcmVlcywgTmV0d29yayBEaWFncmFtcyBhbmQgR2Vub21pYyBSZXByZXNlbnRhdGlvbnMNCg0KIyMgMC4xLjAgQW4gb3ZlcnZpZXcgb2YgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCioqIkFkdmFuY2VkIEdyYXBoaWNzIGFuZCBEYXRhIFZpc3VhbGl6YXRpb24gaW4gUiIqKiBpcyBicm91Z2h0IHRvIHlvdSBieSB0aGUgQ2VudHJlIGZvciB0aGUgQW5hbHlzaXMgb2YgR2Vub21lIEV2b2x1dGlvbiAmIEZ1bmN0aW9uJ3MgKENBR0VGKSBiaW9pbmZvcm1hdGljcyB0cmFpbmluZyBpbml0aWF0aXZlLiBUaGlzIENTQjEwMjEgd2FzIGRldmVsb3BlZCB0byBlbmhhbmNlIHRoZSBza2lsbHMgb2Ygc3R1ZGVudHMgd2l0aCBiYXNpYyBiYWNrZ3JvdW5kcyBpbiBSIGJ5IGZvY3VzaW5nIG9uIGF2YWlsYWJsZSBwaGlsb3NvcGhpZXMsIG1ldGhvZHMsIGFuZCBwYWNrYWdlcyBmb3IgcGxvdHRpbmcgc2NpZW50aWZpYyBkYXRhLiBXaGlsZSB0aGUgZGF0YXNldHMgYW5kIGV4YW1wbGVzIHVzZWQgaW4gdGhpcyBjb3Vyc2Ugd2lsbCBiZSBjZW50cmVkIG9uIFNBUlMtQ29WLTIgZXBpZGVtaW9sb2dpY2FsIGFuZCBnZW5vbWljIGRhdGEsIHRoZSBsZXNzb25zIGxlYXJuZWQgaGVyZWluIHdpbGwgYmUgYnJvYWRseSBhcHBsaWNhYmxlLg0KDQpUaGlzIGxlc3NvbiBpcyB0aGUgc2l4dGggYW5kIGZpbmFsIGxlY3R1cmUgaW4gYSA2LXBhcnQgc2VyaWVzLiBUaGUgYWltIGZvciB0aGUgZW5kIG9mIHRoaXMgc2VyaWVzIGlzIGZvciBzdHVkZW50cyB0byByZWNvZ25pemUgaG93IHRvIGltcG9ydCwgZm9ybWF0LCBhbmQgZGlzcGxheSBkYXRhIGJhc2VkIG9uIHRoZWlyIGludGVuZGVkIG1lc3NhZ2UgYW5kIGF1ZGllbmNlLiBUaGUgZm9ybWF0IGFuZCBzdHlsZSBvZiB0aGVzZSB2aXN1YWxpemF0aW9ucyB3aWxsIGhlbHAgdG8gaWRlbnRpZnkgYW5kIGNvbnZleSB0aGUga2V5IG1lc3NhZ2UocykgZnJvbSB0aGVpciBleHBlcmltZW50YWwgZGF0YS4NCg0KVGhlIHN0cnVjdHVyZSBvZiB0aGUgY2xhc3MgaXMgYSAqKmNvZGUtYWxvbmcgc3R5bGUqKiBpbiBSIG1hcmtkb3duIG5vdGVib29rcy4gQXQgdGhlIHN0YXJ0IG9mIGVhY2ggbGVjdHVyZSwgc2tlbGV0b24gdmVyc2lvbnMgb2YgdGhlIGxlY3R1cmUgd2lsbCBiZSBwcm92aWRlZCBmb3IgdXNlIG9uIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKSBzbyBzdHVkZW50cyBjYW4gcHJvZ3JhbSBhbG9uZyB3aXRoIHRoZSBpbnN0cnVjdG9yLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC4yLjAgTGVjdHVyZSBvYmplY3RpdmVzDQoNClRoaXMgd2VlayB3aWxsIGZvY3VzIG9uIGNvbW1vbmx5IHVzZWQgdmlzdWFsaXphdGlvbnMgcmVsYXRlZCB0byBnZW5vbWljIGluZm9ybWF0aW9uLiBXaGVuIHdvcmtpbmcgd2l0aCBzZXF1ZW5jaW5nIGRhdGEsIHlvdSBtYXkgY29tbW9ubHkgd2lzaCB0byBjb21wYXJlIHNlcXVlbmNlcyBiYXNlZCBvbiB0aGVpciByZWxhdGlvbnNoaXBzIG9yIHJlbGF0aXZlIHNpbWlsYXJpdHkgKHRyZWVzKSwgYnkgdGhlaXIgc2VxdWVuY2UgaWRlbnRpdHkgaW4gZ2VuZSBmYW1pbGllcyBvciBwb3RlbnRpYWwgaW50ZXJhY3Rpb25zIChncmFwaHMpLCBhbmQgb3IgbW9yZSBkaXJlY3RseSB0aGVpciBzZXF1ZW5jZSBtb3RpZnMuDQoNCkF0IHRoZSBlbmQgb2YgdGhpcyBsZWN0dXJlIHlvdSB3aWxsIGhhdmUgY292ZXJlZCB0aGUgZm9sbG93aW5nIHRvcGljcw0KDQoxLiAgUGh5bG9nZW5ldGljIFRyZWVzDQoyLiAgTmV0d29yayBncmFwaHMNCjMuICBHZW5vbWUgc2VxdWVuY2VzIGFuZCBtYXJrZXJzDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjMuMCBBIGxlZ2VuZCBmb3IgdGV4dCBmb3JtYXQgaW4gUiBtYXJrZG93bg0KDQpgZ3JleSBiYWNrZ3JvdW5kYCAtIGEgcGFja2FnZSwgZnVuY3Rpb24sIGNvZGUsIGNvbW1hbmQgb3IgZGlyZWN0b3J5LiBCYWNrdGlja3MgYXJlIGFsc28gdXNlIGZvciBpbi1saW5lIGNvZGUuXA0KKml0YWxpY3MqIC0gYW4gaW1wb3J0YW50IHRlcm0gb3IgY29uY2VwdCBvciBhbiBpbmRpdmlkdWFsIGZpbGUgb3IgZm9sZGVyXA0KKipib2xkKiogLSBoZWFkaW5nIG9yIGEgdGVybSB0aGF0IGlzIGJlaW5nIGRlZmluZWRcDQpbYmx1ZSB0ZXh0XXtzdHlsZT0iY29sb3I6Ymx1ZSJ9IC0gbmFtZWQgb3IgdW5uYW1lZCBoeXBlcmxpbmsNCg0KYC4uLmAgLSBXaXRoaW4gZWFjaCBjb2RpbmcgY2VsbCB0aGlzIHdpbGwgaW5kaWNhdGUgYW4gYXJlYSBvZiBjb2RlIHRoYXQgc3R1ZGVudHMgd2lsbCBuZWVkIHRvIGNvbXBsZXRlIGZvciB0aGUgY29kZSBjZWxsIHRvIHJ1biBjb3JyZWN0bHkuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtaW5mb30NCioqQmx1ZSBib3g6KiogQSBrZXkgY29uY2VwdCB0aGF0IGlzIGJlaW5nIGludHJvZHVjZWQNCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXdhcm5pbmd9DQoqKlllbGxvdyBib3g6KiogUmlzayBvciBjYXV0aW9uDQo6OjoNCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1zdWNjZXNzfQ0KKipHcmVlbiBib3hlczoqKiBSZWNvbW1lbmRlZCByZWFkcyBhbmQgcmVzb3VyY2VzIHRvIGxlYXJuIFINCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWRhbmdlcn0NCioqUmVkIGJveGVzOioqIEEgY29tcHJlaGVuc2lvbiBxdWVzdGlvbiB3aGljaCBtYXkgb3IgbWF5IG5vdCBpbnZvbHZlIGEgY29kaW5nIGNlbGwuIFlvdSB1c3VhbGx5IGZpbmQgdGhlc2UgYXQgdGhlIGVuZCBvZiBhIHNlY3Rpb24uDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuNC4wIExlY3R1cmUgYW5kIGRhdGEgZmlsZXMgdXNlZCBpbiB0aGlzIGNvdXJzZQ0KDQojIyMgMC40LjEgV2Vla2x5IExlY3R1cmUgYW5kIHNrZWxldG9uIGZpbGVzDQoNCkVhY2ggd2VlaywgbmV3IGxlc3NvbiBmaWxlcyB3aWxsIGFwcGVhciB3aXRoaW4geW91ciBSU3R1ZGlvIGZvbGRlcnMuIFdlIGFyZSBwdWxsaW5nIGZyb20gYSBHaXRIdWIgcmVwb3NpdG9yeSB1c2luZyB0aGlzIFtSZXBvc2l0b3J5IGdpdC1wdWxsIGxpbmtdKGh0dHBzOi8vci5kYXRhdG9vbHMudXRvcm9udG8uY2EvaHViL3VzZXItcmVkaXJlY3QvZ2l0LXB1bGw/cmVwbz1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZjYW1vayUyRjIwMjQtMDMtQWR2X0dyYXBoaWNzX1ImdXJscGF0aD1yc3R1ZGlvJTJGJmJyYW5jaD1tYWluKS4gU2ltcGx5IGNsaWNrIG9uIHRoZSBsaW5rIGFuZCBpdCB3aWxsIHRha2UgeW91IHRvIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKS4gWW91IHdpbGwgbmVlZCB0byB1c2UgeW91ciBVVE9SaWQgY3JlZGVudGlhbHMgdG8gY29tcGxldGUgdGhlIGxvZ2luIHByb2Nlc3MuIEZyb20gdGhlcmUgeW91IHdpbGwgZmluZCBlYWNoIHdlZWsncyBsZWN0dXJlIGZpbGVzIGluIHRoZSBkaXJlY3RvcnkgYC8yMDI0LTAzLUFkdl9HcmFwaGljc19SL0xlY3R1cmVfWFhgLiBZb3Ugd2lsbCBmaW5kIGEgcGFydGlhbGx5IGNvZGVkIGBza2VsZXRvbi5SbWRgIGZpbGUgYXMgd2VsbCBhcyBhbGwgb2YgdGhlIGRhdGEgZmlsZXMgbmVjZXNzYXJ5IHRvIHJ1biB0aGUgd2VlaydzIGxlY3R1cmUuDQoNCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gZG93bmxvYWQgdGhlIFItTWFya2Rvd24gTm90ZWJvb2sgKGAuUm1kYCkgYW5kIGRhdGEgZmlsZXMgZnJvbSB0aGUgUlN0dWRpbyBzZXJ2ZXIgdG8geW91ciBwZXJzb25hbCBjb21wdXRlciBpZiB5b3Ugd291bGQgbGlrZSB0byBydW4gaW5kZXBlbmRlbnRseSBvZiB0aGUgVG9yb250byB0b29scy4NCg0KIyMjIDAuNC4yIExpdmUtY29kaW5nIEhUTUwgcGFnZQ0KDQpBIGxpdmUgbGVjdHVyZSB2ZXJzaW9uIHdpbGwgYmUgYXZhaWxhYmxlIGF0IFtjYW1vay5naXRodWIuaW9dKGh0dHBzOi8vY2Ftb2suZ2l0aHViLmlvLzIwMjQtMDMuQWR2X0dyYXBoaWNzX1IvaW5kZXguaHRtbCkgdGhhdCB3aWxsIHVwZGF0ZSBhcyB0aGUgbGVjdHVyZSBwcm9ncmVzc2VzLiBCZSBzdXJlIHRvIHJlZnJlc2ggdG8gdGFrZSBhIGxvb2sgaWYgeW91IGdldCBsb3N0IQ0KDQojIyMgMC40LjMgUG9zdC1sZWN0dXJlIFBERnMNCg0KQXMgbWVudGlvbmVkIGFib3ZlLCBhdCB0aGUgZW5kIG9mIGVhY2ggbGVjdHVyZSB0aGVyZSB3aWxsIGJlIGEgY29tcGxldGVkIHZlcnNpb24gb2YgdGhlIGxlY3R1cmUgY29kZSByZWxlYXNlZCBhcyBhIFBERiBmaWxlIHVuZGVyIHRoZSBNb2R1bGVzIHNlY3Rpb24gb2YgUXVlcmN1cy4NCg0KIyMgMC40LjQgRGF0YSB1c2VkIGluIHRoaXMgbGVzc29uDQoNClRvZGF5J3MgZGF0YXNldHMgd2lsbCBmb2N1cyBvbiBTQVJTLUNvVi0yIHZhcmlhbnQgc3VydmVpbGxhbmNlIGRhdGEgZnJvbSB0aGUgd2hpY2ggaGFzIGJlZW4gdHJhY2tpbmcgcHVibGlzaGVkIHNlcXVlbmNlZCBnZW5vbWVzIGZvciB0aGUgYXBwZWFyYW5jZSBvZiBuZXcgc3RyYWlucyBpbiBOb3J0aCBBbWVyaWNhLg0KDQojIyMgMC40LjQuMSBEYXRhc2V0IDE6IG5leHRzdHJhaW5fbmNvdl9ub3J0aC1hbWVyaWNhX2NhbmFkYV90aW1ldHJlZS5ud2sNCg0KVGhpcyBpcyBhIE5ld2ljayBmb3JtYXQgZGF0YSBzZXQgZGVzY3JpYmluZyBhIHBoeWxvZ2VuZXRpYyB0cmVlIG9mIFNBUlMtQ29WLTIgc3RyYWluIGluZm9ybWF0aW9uLg0KDQojIyMgMC40LjQuMiBEYXRhc2V0IDI6IG5leHRzdHJhaW5fbmNvdl9ub3J0aC1hbWVyaWNhX2NhbmFkYV9tZXRhZGF0YS50c3YNCg0KTWV0YWRhdGEgdGhhdCBhY2NvbXBhbmllcyB0aGUgZmlyc3QgZGF0YXNldC4gSXQgbGlua3Mgc3RyYWluIGluZm9ybWF0aW9uIGJhY2sgdG8gYXMgbXVjaCBnZW9ncmFwaGljYWwgYW5kIHJlbGF0ZWQgaW5mb3JtYXRpb24gYXMgcG9zc2libGUuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjUuMCBQYWNrYWdlcyB1c2VkIGluIHRoaXMgbGVzc29uDQoNCmB0aWR5dmVyc2VgIHdoaWNoIGhhcyBhIG51bWJlciBvZiBwYWNrYWdlcyBpbmNsdWRpbmcgYGRwbHlyYCwgYHRpZHlyYCwgYHN0cmluZ3JgLCBgZm9yY2F0c2AgYW5kIGBnZ3Bsb3QyYA0KDQpgdmlyaWRpc2AgaGVscHMgdG8gY3JlYXRlIGNvbG9yLWJsaW5kIHBhbGV0dGVzIGZvciBvdXIgZGF0YSB2aXN1YWxpemF0aW9ucw0KDQpgUkNvbG9yQnJld2VyYCBoYXMgc29tZSBobGVwZnVsIHBhbGV0dGVzIHRoYXQgd2UnbGwgbmVlZCB0byBjb2xvdXIgb3VyIGRhdGEuDQoNCmBnZ25ld3NjYWxlYCB3aWxsIGJlIGhlbHBmdWwgaW4gZ2VuZXJhdGluZyBtdWx0aXBsZSBjb2xvdXIgcGFsZXR0ZXMgYWNyb3NzIGluY3JlYXNpbmdseSBjb21wbGV4IHBsb3RzLg0KDQpgdHJlZWlvYCwgYHRpZHl0cmVlYCwgYW5kIGBnZ3RyZWVgIHdpbGwgYmUgdXNlZCB0byBoZWxwIGltcG9ydCwgcGFyc2UgYW5kIHBsb3QgcGh5bG9nZW5ldGljIHRyZWVzLg0KDQpgdGlkeWdyYXBoYCBhbmQgYGdncmFwaGAgd2lsbCBiZSB1c2VkIHRvIGdlbmVyYXRlIG5ldHdvcmsgZ3JhcGggb2JqZWN0cyBhbmQgcGxvdCB0aGVtLg0KDQpgZ2dzZXFsb2dvYCBpcyB1c2VkIGZvciBnZW5lcmF0aW5nIHNlcXVlbmNlIGxvZ29zIHJlbGF0ZWQgdG8gc2VxdWVuY2UgbW90aWZzLg0KDQpgcXFtYW5gIGlzIGEgd3JhcHBlciBwYWNrYWdlIGZvciBnZW5lcmF0aW5nIE1hbmhhdHRhbiBwbG90cy4NCg0KYGx1YnJpZGF0ZWAgYW5kIGB6b29gIGhlbHAgdXMgdG8gd29yayB3aXRoIHNvbWUgZGF0ZS1iYXNlZCBpbmZvcm1hdGlvbi4NCg0KYGBge3J9DQojIFdoZW4gZG8gd2Ugc3RhcnQgaW5zdGFsbGluZyB0aGUgcGFja2FnZXMNClN5cy50aW1lKCkNCg0KIyBOZXcgYmlvY29uZHVjdG9yIHBhY2thZ2VzIHdlIGhhdmVuJ3Qgd29ya2VkIHdpdGggYmVmb3JlDQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCg0KQmlvY01hbmFnZXI6Omluc3RhbGwoImdnbmV3c2NhbGUiLCB1cGRhdGUgPSBGQUxTRSkNCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJnZ3RyZWUiLCB1cGRhdGUgPSBGQUxTRSkNCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJnZ3RyZWVFeHRyYSIsIHVwZGF0ZSA9IEZBTFNFKQ0KDQojIE5ldyBDUkFOIHBhY2thZ2VzIHdlIGhhdmVuJ3Qgd29ya2VkIHdpdGggYmVmb3JlDQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dHJlZSIpDQppbnN0YWxsLnBhY2thZ2VzKCJnZ3NlcWxvZ28iKQ0KaW5zdGFsbC5wYWNrYWdlcygicXFtYW4iKQ0KaW5zdGFsbC5wYWNrYWdlcygiZ2dyYXBoIikNCmluc3RhbGwucGFja2FnZXMoInRpZHlncmFwaCIpDQoNCiMgV2hlbiBkbyB3ZSBmaW5pc2g/DQpTeXMudGltZSgpDQpgYGANCg0KYGBge3J9DQojIFBhY2thZ2VzIHRvIGhlbHAgdGlkeSBvdXIgZGF0YQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWR4bCkNCg0KIyBQYWNrYWdlcyBmb3IgdGhlIGdyYXBoaWNhbCBhbmFseXNpcyBzZWN0aW9uDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZ2duZXdzY2FsZSkNCg0KIyBQYWNrYWdlcyBmb3IgdG9kYXkncyBsZWN0dXJlIGFib3V0IHRyZWVzDQpsaWJyYXJ5KHRpZHl0cmVlKQ0KbGlicmFyeShnZ3RyZWUpDQpsaWJyYXJ5KGdndHJlZUV4dHJhKQ0KbGlicmFyeSh0cmVlaW8pDQoNCiMgUGFja2FnZXMgZm9yIGdyYXBocw0KbGlicmFyeShnZ3JhcGgpDQpsaWJyYXJ5KHRpZHlncmFwaCkNCg0KIyBQYWNrYWdlcyBmb3Igc2VxdWVuY2UgbG9nb3MNCmxpYnJhcnkoZ2dzZXFsb2dvKQ0KDQojIFBhY2thZ2VzIGZvciBNYW5oYXR0YW4gcGxvdHMNCmxpYnJhcnkocXFtYW4pDQoNCiMgRGF0ZSBjYWxjdWxhdGlvbiBoZWxwZXJzDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoem9vKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDEuMC4wIFdoYXQncyBpbiBhIG5hbWU/IEEgcGh5bG9nZW5ldGljIHRyZWUgaXMganVzdCBhIHJlYnJhbmRlZCBkZW5kcm9ncmFtDQoNCkRlbmRyb2dyYW1zLCBjbGFkb2dyYW1zLCBhbmQgcGh5bG9nZW5ldGljIHRyZWVzIGFsbCBzaGFyZSBhIHNpbWlsYXIgc3RydWN0dXJlIHdpdGggc29tZSBtb2RpZmljYXRpb25zLiBBbGwgYXJlIHJlcHJlc2VudGVkIGluIGJyYW5jaGluZyB0cmVlIHN0cnVjdHVyZXMgdGhhdCBhcmUgdXNlZCB0byByZXByZXNlbnQgdGhlIHJlbGF0aW9uc2hpcHMgYW1vbmcgdGhlIGxlYXZlcywgYWxzbyBrbm93biBhcyAqKnRpcHMqKi4gQnJhbmNoZXMgYWxvbmcgdGhlIHRyZWUgbWF5IGFsc28gYmUgcmVmZXJyZWQgdG8gYXMgKiplZGdlcyoqLg0KDQpMZWF2ZXMgY2FuIHJlcHJlc2VudCBkaWZmZXJlbnQgc3BlY2llcywgc3RyYWlucywgc2VxdWVuY2VzIG9yIG11bHRpLWRpbWVuc2lvbmFsIHZhbHVlcy4gTGVhdmVzIGFyZSBjb25uZWN0ZWQgYnkgYnJhbmNoZXMgdG8gdGhlaXIgbmVhcmVzdCBuZWlnaGJvdXJzIG9yIHJlbGF0aXZlcy4gQXMgeW91IG1vdmUgYmFja3dhcmRzIGFsb25nIHRoZSB0cmVlLCB5b3UgZW5jb3VudGVyIGludGVybmFsICoqbm9kZXMqKiB3aGljaCBjYW4gY29ubmVjdCBkaXJlY3RseSB0byBtb3JlIHRpcHMgb3IgbW9yZSBub2Rlcy4gVGhlIGRpc3RhbmNlIGJldHdlZW4gdGlwcyBhbmQgbm9kZXMgb24gYSBwaHlsb2dlbmV0aWMgdHJlZSwgcmVwcmVzZW50IGEgcmVsYXRpdmUgZGlzdGFuY2UgdGhhdCBtYXkgYmUgZGVmaW5lZCBhcyBzb21lIHR5cGUgb2YgZXZvbHV0aW9uYXJ5IGRpc3RhbmNlLCB0aW1lLCBvciBzaW1wbGUgZXVjbGlkZWFuIGRpc3RhbmNlLiBJbiBhIGNsYWRvZ3JhbSwgaG93ZXZlciwgZGlzdGFuY2VzIGFsb25nIGJyYW5jaGVzIGhhdmUgbm8gbWVhbmluZyBhbmQgb25seSBkZWZpbmUgdGhlIHByZXNlbmNlIG9mIGEgcmVsYXRpb25zaGlwLg0KDQpJbiB0cmVlIHRlcm1pbm9sb2d5LCBpdCBpcyBoZWxwZnVsIHRvIGRlZmluZSBhIGZldyBtb3JlIHRlcm1zOg0KDQp8IFRlcm0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgKipSb290KiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBBIHRyZWUgaXMgcm9vdGVkIHdoZW4gaXQgZGVmaW5lcyBhIGNvbW1vbiBhbmNlc3RvciBmb3IgYWxsIHRpcHMgaW4gdGhlIHRyZWUuIFRoaXMgY291bGQgYmUgY29uc2lkZXJlZCB0aGUgc3RhcnQgb3IgZW50cnkgcG9pbnQgZm9yIHRoZSB0cmVlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgKipNb3N0IHJlY2VudCBjb21tb24gYW5jZXN0ZXIgKE1SQ0EpKiogfCBUaGlzIGlzIGFuIGludGVybmFsIG5vZGUgdGhhdCBjb2xsZWN0aXZlbHkgam9pbnMgdHdvIG9yIG1vcmUgdGlwcy4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgKipDbGFkZSoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBBIGdyb3VwIG9mIHRheGEgKHRpcHMpIHRoYXQgaW5jbHVkZXMgYSBjb21tb24gYW5jZXN0b3IgYW5kICphbGwqIG9mIGl0cyBkZXNjZW5kYW50cy4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgKipTY2FsaW5nKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSBicmFuY2ggbGVuZ3RocyAqZG8qIHJlcHJlc2VudCBhIGRpc3RhbmNlIG1ldHJpYywgd2hlcmVhcywgYW4gdW5zY2FsZWQgdHJlZSBtYXkgaGF2ZSBldmVuLWxlbmd0aCBicmFuY2hlcyBub3QgcmVwcmVzZW50YXRpdmUgb2YgZXZvbHV0aW9uYXJ5L3JlbGF0aW9uc2hpcCBkaXN0YW5jZXMuIHwNCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9QaHlsb2dlbmV0aWMtVHJlZS01NDJ4NDIwLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KDQpEaWFncmFtIG9mIHBoeWxvZ2VuZXRpYyB0cmVlIHRlcm1zIGZyb20gWzEwIGRpZmZlcmVuY2VzIGJldHdlZW4gY2xhZG9ncmFtcyBhbmQgcGh5bG9nZW5ldGljcyB0cmVlc10oaHR0cHM6Ly92aXZhZGlmZmVyZW5jZXMuY29tL2RpZmZlcmVuY2UtYmV0d2Vlbi1jbGFkb2dyYW0tYW5kLXBoeWxvZ2VuZXRpYy10cmVlLykNCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS4xLjAgV2hlcmUgZG9lcyB0cmVlIGRhdGEgY29tZSBmcm9tPw0KDQpBcyB3ZSBzYXcgbGFzdCB3ZWVrLCBjbHVzdGVyaW5nIGFuYWx5c2lzIHN1Y2ggYXMgdGhhdCBmcm9tIGBoY2x1c3QoKWAgY2FuIGNyZWF0ZSBkZW5kcm9ncmFtIGRhdGEuIFdlIHBsb3R0ZWQgdGhpcyBpbiBhIGNvdXBsZSBvZiB3YXlzIGJ5IGl0c2VsZiB1c2luZyBgZnZpel9kZW5kKClgIGZyb20gdGhlIGBmYWN0b2V4dHJhYCBwYWNrYWdlIGFuZCB3aXRoIGBIZWF0bWFwKClgIGZyb20gdGhlIGBDb21wbGV4SGVhdG1hcGAgcGFja2FnZS4gSW4gdGhlIGNhc2Ugb2Ygb3VyIGZpcnN0IGZvcmF5LCB3ZSB3ZXJlIGxvb2tpbmcgYXQgdGhlICJyZWxhdGlvbnNoaXBzIiBiZXR3ZWVuIHNhbXBsZXMgYnkgaW5kaWNhdGluZyBob3cgc2ltaWxhciB0aGV5IHdlcmUgYmFzZWQgb24gdGhlIGNoYXJhY3RlcmlzdGljcyBpbiB0aGVpciB2YXJpb3VzIGZlYXR1cmVzLg0KDQpEZXBlbmRpbmcgb24gdGhlIHNvZnR3YXJlIHVzZWQsIHRyZWVzIGNhbiBiZSByZXByZXNlbnRlZCBpbiBhIG51bWJlciBvZiBmb3JtYXRzLCBzb21lIG9mIHdoaWNoIGFyZSBkZXNjcmliZWQgYmVsb3cuDQoNCnwgRm9ybWF0ICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgTmV3aWNrICAgICAgIHwgVGhlIHN0YW5kYXJkIHVzZWQgdG8gcmVwcmVzZW50IHRyZWVzIGluIGEgY29tcHV0ZXItcmVhZGFibGUgZm9ybWF0LiBUcmVlcyBhcmUgZW5jb2RlZCBpbiBhIHBhcmVudGhlc2VzLWNsb3N1cmUgZm9ybWF0IHdoZXJlIGVhY2ggdGlwIHRha2VzIHRoZSBmb3JtIG9mICIqKnRheGE6YnJhbmNoLWxlbmd0aCoqIiAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICB8IGFuZCBwYWlycyBvZiB0aXBzIGFyZSBzZXBhcmF0ZWQgYnkgYSBjb21tYSBgLGAgYW5kIGVuY2xvc2VkIGJ5IHBhcmVudGhlc2VzIGAoKWAuIEludGVybmFsIG5vZGVzIGJyYW5jaCBsZW5ndGhzIGFyZSBkZWZpbmVkIG91dHNpZGUgdGhlIHBhcmVudGhlc2VzIHdpdGggIioqKCk6YnJhbmNoLWxlbmd0aCoqIiAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBORVhVUy9waHlsaXAgfCBJbmNvcnBvcmF0ZXMgdGhlIE5ld2ljayB0cmVlIHdpdGggc2VwYXJhdGUgcmVsYXRlZCBkYXRhIHRoYXQgaXMgcGFydGl0aW9uZWQgaW50byBkaWZmZXJlbnQgYmxvY2tzLiBCbG9ja3MgYXJlIHN0YXJ0ZWQgd2l0aCAiKipCRUdJTiBcPEJMT0NLIE5BTUVcPjsqKiIgYW5kIGNsb3NlZCB3aXRoICIqKkVORDsqKiIgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgTkhYICAgICAgICAgIHwgTmV3IEhhbXBzaGlyZSBlWHRlbmRlZCBmb3JtYXQgaXMgYWxzbyBiYXNlZCBvbiBOZXdpY2sgZm9ybWF0IGJ1dCBpbnN0ZWFkIG9mIGNvZGUgYmxvY2tzIHVzZXMgYSB0YWdnaW5nIHN5c3RlbSBmb3IgZWFjaCBub2RlIGV4dGVuZGluZyB0aGUgZm9ybWF0IHRvICIqKnRheGE6YnJhbmNoLWxlbmd0aFsmJk5IWDpcPHRhZyBpbmZvcm1hdGlvblw+XSoqIiB8DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjIuMCBVc2luZyBgcmVhZC50cmVlKClgIGZyb20gdGhlIGB0cmVlaW9gIHBhY2thZ2UNCg0KV2hlbiBvcGVuaW5nIHRyZWUgZmlsZXMsIGRlcGVuZGluZyBvbiB0aGUgZm9ybWF0LCB5b3UgY2FuIHVzZSB0aGUgYHRyZWVpb2AgcGFja2FnZSBhbmQgb25lIG9mIGl0J3MgbWFueSBbcGFyc2luZyBmdW5jdGlvbnNdKGh0dHBzOi8veXVsYWItc211LnRvcC90cmVlZGF0YS1ib29rL2NoYXB0ZXIxLmh0bWwpIGJ1dCB0aGUgc2ltcGxlc3Qgd2F5IHRvIGF2b2lkIGZpZ3VyaW5nIG91dCBob3cgdG8gb3BlbiBhIHRyZWUgZmlsZSwgaXMgdG8ganVzdCB1c2UgdGhlIGByZWFkLnRyZWUoKWAgZnVuY3Rpb24uIFRoaXMgd2lsbCBkZXRlcm1pbmUgdGhlIGFwcHJvcHJpYXRlIGZpbGUgdHlwZSBhbmQgcGFyc2UgdGhyb3VnaCB0aGUgbWFueSBkaWZmZXJlbnQgdHJlZSBmb3JtYXRzIGJlZm9yZSBkZXBvc2l0aW5nIHRoZSBkYXRhIGludG8gYSBgcGh5bG9gIG9iamVjdC4NCg0KVG9kYXkgd2UnbGwgYmUgd29ya2luZyB3aXRoIGEgZGF0YXNldCBmcm9tIHRoZSBbQXVzcGljZSBDT1ZJRC0xOSBOb3J0aCBBbWVyaWNhbiBkYXRhc2V0XShodHRwOi8vYXVzcGljZS5maW5sYXltYWd1aS5yZS9uY292L25vcnRoLWFtZXJpY2EvY2FuYWRhL29udGFyaW8pIG1haW50YWluZWQgYnkgW0ZpbmxheSBNYWd1aXJlXShodHRwczovL2ZpbmxheW1hZ3VpLnJlLykuICoqVW5mb3J0dW5hdGVseSB0aGlzIGRhdGFzZXQgaXMgbm8gbG9uZ2VyIGF2YWlsYWJsZSBidXQgd2UgaGF2ZSBhbiBvbGRlciBjb3B5IHdpdGggYSBmYWlyIGFtb3VudCBvZiBtZXRhZGF0YS4qKg0KDQpMZXQncyBiZWdpbiBieSBvcGVuaW5nIHVwIHRoZSB0cmVlIGRhdGEgYW5kIHNvbWUgcmVsYXRlZCBtZXRhZGF0YSBiZWZvcmUgdGFraW5nIGEgbG9vayB1bmRlciB0aGUgaG9vZC4NCg0KYGBge3J9DQojIEltcG9ydCBvdXIgTmV3aWNrIFRyZWUNClNDMl92YXJpYW50c190aW1lLnBoeWxvIDwtIHJlYWQudHJlZSgiLi9kYXRhL25leHRzdHJhaW5fbmNvdl9ub3J0aC1hbWVyaWNhX2NhbmFkYV90aW1ldHJlZS5ud2siKQ0KDQojIEltcG9ydCBvdXIgbWV0YWRhdGENClNDMl9tZXRhZGF0YS5kZiA8LSByZWFkX3RzdigiZGF0YS9uZXh0c3RyYWluX25jb3Zfbm9ydGgtYW1lcmljYV9jYW5hZGFfbWV0YWRhdGEudHN2IikNCmBgYA0KDQpgYGB7cn0NCiMgV2hhdCBkb2VzIHRoZSB0cmVlIGxvb2sgbGlrZT8NCnN0cihTQzJfdmFyaWFudHNfdGltZS5waHlsbykNCg0KU0MyX3ZhcmlhbnRzX3RpbWUucGh5bG8NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuMi4xIFRoZSBgcGh5bG9gIG9iamVjdCBpcyBhIHNpbXBsZSBsaXN0DQoNCkZyb20gdGhlIGxvb2tzIG9mIG91ciBzdHJ1Y3R1cmUsIGFmdGVyIHJlYWRpbmcgaW4gb3VyIE5ld2ljayBmaWxlLCB3ZSBoYXZlIGEgbGlzdCB3aXRoIHNpeCBlbGVtZW50cy4gVGhleSBhcmUgcHJldHR5IGNsZWFybHkgbmFtZWQgd2l0aCBlZGdlIGluZm9ybWF0aW9uIChhIG1hdHJpeCBvZiBwYWlyZWQgbnVtYmVycyksIGVkZ2UgbGVuZ3RocywgYSBjb3VudCBvZiB0aGUgbnVtYmVyIG9mIG5vZGVzIChpZSAqaW50ZXJuYWwqIHBvaW50cyB3aGVyZSBicmFuY2hlcyBiaWZ1cmNhdGUuIFdoaWxlIG1vc3RseSBqdXN0IG51bWJlcnMsIHNvbWUgb2YgdGhlc2UgYG5vZGUubGFiZWxgIHZhbHVlcyBhcHBlYXIgdG8gYmUgYmFzZWQgb24gdHJhdmVsIGhpc3RvcnkgaW5mb3JtYXRpb24uIEFsbCBvZiB0aGUgNiw2MjcgYHRpcC5sYWJlbGAgdmFsdWVzIGNvcnJlc3BvbmQgdG8gNiw2MjcgU0FSUy1Db1YtMiBzdHJhaW4gbmFtZXMgZm91bmQgaW4gdGhlIG1ldGFkYXRhIGZpbGUgYXMgd2VsbC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjIuMiBDb252ZXJ0IHRoZSB0cmVlIG9iamVjdCB0byBhIHRpYmJsZSBmb3IgYWRkaW5nIGV4dGVybmFsIGluZm9ybWF0aW9uDQoNClN1cHBvc2Ugd2Ugd2FudCB0byBjb21iaW5lIHNvbWUgaW5mb3JtYXRpb24gZnJvbSBvdXIgbWV0YWRhdGEgd2l0aCBvdXIgdHJlZT8gV2UgY2FuIHRoZW4gdXNlIHRoaXMgaW5mb3JtYXRpb24gdG8gY29sb3VyLCBzaGFwZSwgYW5kIGJyaW5nIG1vcmUgdmlzdWFsIG9yZGVyIHRvIG91ciB0cmVlLiBXb3JraW5nIHdpdGggdGhlIHRyZWUsIGhvd2V2ZXIsIGNhbiBiZSBhIGxpdHRsZSBoYXJkIGdpdmVuIHRoYXQgd2UgaGF2ZSBsaXN0cyBvZiBkaWZmZXJlbnQgbGVuZ3RocyByZXByZXNlbnRpbmcgZGlmZmVyZW50IHBvcnRpb25zIG9mIHRoZSB0cmVlLg0KDQpJbiBvdXIgY2FzZSwgd2Ugb25seSBoYXZlIHRyZWUgdGlwIGluZm9ybWF0aW9uIHRoYXQgd2Ugd2FudCB0byBhZGQgdG8gYW5kIHRoZSBlYXNpZXN0IHdheSB0byB3b3JrIHdvdWxkIGJlIGlmIGl0IHdhcyBpbiBhIHRhYmxlIGZvcm1hdC4gVGhlIHBhY2thZ2UgYHRpZHl0cmVlYCBwcm92aWRlcyBhIGZ1bmN0aW9uIGBhc190aWJibGVgIHRoYXQgd2lsbCBwYXJzZSBhbmQgY29udmVydCB0aGUgYHBoeWxvYCBmb3JtYXQgdG8gYSBgdGJsX3RyZWVgIG9iamVjdCwgd2hpY2ggaXMgYSBraW5kIG9mIHRpZHkgZGF0YSBmcmFtZS4NCg0KTGV0J3MgY29udmVydCBvdXIgdmFyaWFudCBwaHlsbyBvYmplY3QgdG8gc29tZXRoaW5nIHdlIGNhbiB3b3JrIHdpdGguDQoNCmBgYHtyfQ0KIyBDb252ZXJ0IHRoZSBwaHlsbyBvYmplY3QgdXNpbmcgYXNfdGliYmxlDQpTQzJfdmFyaWFudHNfdGltZS50YiA8LSBhc190aWJibGUoU0MyX3ZhcmlhbnRzX3RpbWUucGh5bG8pDQoNCiMgQ2hlY2sgb3V0IHRoZSBzdHJ1Y3R1cmUgbm93DQpzdHIoU0MyX3ZhcmlhbnRzX3RpbWUudGIpDQoNCmhlYWQoU0MyX3ZhcmlhbnRzX3RpbWUudGIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuMy4wIFVwZGF0aW5nIG91ciB0cmVlIHdpdGggZXh0ZXJuYWwgaW5mb3JtYXRpb24NCg0KQXMgeW91IGNhbiBzZWUgb3VyIGBTQzJfdmFyaWFudHNfdGltZS50YmAgb2JqZWN0IGlzIGEgc2ltcGxlIGRhdGEgZnJhbWUgbm93IHJlcHJlc2VudGVkIGFzIGEgNC1jb2x1bW4gdGFibGUgd2l0aCAxMiw3Nzcgcm93cy4gVGhlIGVkZ2UgZGF0YSBpcyBlbmNvZGVkIGJldHdlZW4gdGhlIGBwYXJlbnRgIGFuZCBgbm9kZWAgY29sdW1ucyB3aXRoIGBicmFuY2gubGVuZ3RoYCB0byBkZWZpbmUgdGhlIGxlbmd0aCBvZiBlYWNoIGVkZ2UuIFdlIGFsc28gaGF2ZSBhbGwgb2YgdGhlIHRpcCBhbmQgbm9kZSBuYW1lcyBzdG9yZWQgdW5kZXIgdGhlIGBsYWJlbGAgY29sdW1uLiBXaXRoIHRoZSB0cmVlIGluIHRoaXMgZm9ybWF0LCB3ZSBjYW4gbm93IHRyZWF0IHRoZSB0cmVlIGxpa2UgYSB0aWJibGUgYW5kIGpvaW4gYWRkaXRpb25hbCBpbmZvcm1hdGlvbiB0byB0aGUgdHJlZS4NCg0KUnVsZXMgdG8ga2VlcCBpbiBtaW5kOg0KDQoxLiAgRG9uJ3QgbG9zZSBhbnkgbm9kZSBpbmZvcm1hdGlvbiBmcm9tIHlvdXIgb3JpZ2luYWwgdHJlZSBzdHJ1Y3R1cmUuIExvc2luZyAib2JzZXJ2YXRpb25zIiBpcyBlc3NlbnRpYWxseSBsb3NpbmcgZWRnZXMgb2YgeW91ciB0cmVlIQ0KDQoyLiAgVHJ5IHRvIHdvcmsgd2l0aCBhIGNvbXBsZXRlIGRhdGFzZXQgb2YgaW5mb3JtYXRpb24gYWx0aG91Z2ggc29tZXRpbWVzIGl0IGRvZXNuJ3QgbWFrZSBzZW5zZSB0aGF0IHlvdSB3b3VsZCBoYXZlIGl0IGFsbC4NCg0KMy4gIENvbnZlcnQgeW91ciB0aWJibGUgYmFjayB0byBhIHRyZWUgZm9ybWF0IHdoZW4geW91J3JlIGRvbmUgd2l0aCBgYXMudHJlZWRhdGEoKWANCg0KWW91IG1heSBoYXZlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBsZWF2ZXMgb3IgdGlwcyBvZiB5b3VyIHRyZWUgYnV0IGJ5IGRlZmF1bHQgdGhlcmUgaXMgbm8gcmVhbCBpbnRlcm5hbCBub2RlIGluZm9ybWF0aW9uIHdoZW4gaXQgY29tZXMgdG8gc2FtcGxlIG9yaWdpbnMgb3IgbGluZWFnZS4gV2hlbiBqb2luaW5nIGluZm9ybWF0aW9uIHRvIHRoZSB0YWJsZXMsIHVzZSB0aGUgY29ycmVjdCBkaXJlY3Rpb24gYCpfam9pbigpYCB0byBhdm9pZCBkYXRhIGxvc3MuIEEgYGZ1bGxfam9pbigpYCBpcyBwcm9iYWJseSB0aGUgc2FmZXN0Lg0KDQpMZXQncyB0YWtlIGEgcXVpY2sgbG9vayBhdCBvdXIgbWV0YWRhdGEgYW5kIGlkZW50aWZ5IHdoYXQgd2UncmUgaW50ZXJlc3RlZCBpbi4NCg0KYGBge3J9DQojIExvb2sgYXQgdGhlIG1ldGFkYXRhLCB3aGljaCBiaXRzIG9mIGluZm9ybWF0aW9uIHdvdWxkIGJlIG5pY2UgdG8gYWRkPw0Kc3RyKFNDMl9tZXRhZGF0YS5kZiwgZ2l2ZS5hdHRyID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQojIENoZWNrIG91dCBzb21lIG9mIHRoZSB2YWx1ZXMgZm9yIHRoZXNlIHZhcmlhYmxlcw0KDQojIE1ldGFkYXRhIGluZm9ybWF0aW9uIG9uIHRoZSBzYW1wbGVzDQp1bmlxdWUoU0MyX21ldGFkYXRhLmRmJENvdW50cnkpDQoNCnVuaXF1ZShTQzJfbWV0YWRhdGEuZGYkQWdlKQ0KDQojIE1ldGFkYXRhIGluZm9ybWF0aW9uIG9uIHRoZSBnZW5vbWVzDQp1bmlxdWUoU0MyX21ldGFkYXRhLmRmJCdHSVNBSUQgY2xhZGUnKQ0KDQp1bmlxdWUoU0MyX21ldGFkYXRhLmRmJCdOZXh0c3RyYWluIGNsYWRlJykNCg0KdW5pcXVlKFNDMl9tZXRhZGF0YS5kZiRDbGFkZSkNCg0KdW5pcXVlKFNDMl9tZXRhZGF0YS5kZiQnUEFOR08gbGluZWFnZScpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjMuMSBQaWNrIHRoZSB0aXAgYXR0cmlidXRlcyB0aGF0IGFyZSBtb3N0IGludGVyZXN0aW5nIG9yIHJlbGV2YW50DQoNCkZvciBvdXIgcHVycG9zZXMgaXQgbG9va3MgbGlrZSB3ZSB3aWxsIHdhbnQgdG8gZXhwbG9yZSBvdXIgZGF0YSB3aXRoOg0KDQotICAgR0lTQUlEIGNsYWRlOiBBICoqZyoqbG9iYWwgKippKipuaXRpYXRpdmUgb24gKipzKipoYXJpbmcgKiphKip2aWFuICoqaSoqbmZsdWVuemEgKipkKiphdGEgc2NpZW50aWZpYyBjb25zb3J0aXVtIG9mIGNsYWRlIG5hbWluZy4NCg0KLSAgIE5leHRzdHJhaW4gY2xhZGU6IENvbXB1dGF0aW9uYWxseSBsYWJlbGVkIGNsYWRlIGluZm9ybWF0aW9uIGJhc2VkIG9uIHRoZSBbTmV4dHN0cmFpbl0oaHR0cHM6Ly9uZXh0c3RyYWluLm9yZy8pIGNyaXRlcmlhIGFuZCBhbmFseXNpcyBvZiBuZXcgYW5kIGNvbnRpbnVpbmcgc3RyYWlucyBmcm9tIENPVklEIGdlbm9taWMgZGF0YS4NCg0KLSAgIEVtZXJnaW5nIE5leHRzdHJhaW4gY2xhZGUuDQoNCi0gICBQQU5HTyBsaW5lYWdlOiBhIGR5bmFtaWMgbm9tZW5jbGF0dXJlIG9mIFtTQVJTLUNvVi0yIGxpbmVhZ2VzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QaHlsb2dlbmV0aWNfQXNzaWdubWVudF9vZl9OYW1lZF9HbG9iYWxfT3V0YnJlYWtfTGluZWFnZXMpLg0KDQotICAgQ291bnRyeTogVGhlIGNvdW50cnkgd2hlcmUgdGhlIHNhbXBsZSB3YXMgY29sbGVjdGVkLg0KDQotICAgQWRtaW4gRGl2aXNpb246IGdlb2dyYXBoaWMgbG9jYXRpb24gb2YgdGhlIHBhcnRpY3VsYXIgc3RyYWluL2Nhc2UuDQoNCi0gICBBZ2U6IHRoZSBhZ2Ugb2YgdGhlIHBhdGllbnRzIGZyb20gd2hpY2ggdGhlIHNhbXBsZSB3YXMgY29sbGVjdGVkIChpZiBpbmNsdWRlZCkNCg0KLSAgIENvbGxlY3Rpb24gRGF0YTogdGhlIGRhdGUgdGhlIGRhdGEgd2FzIHB1Ymxpc2hlZCBvciBjb2xsZWN0ZWQuDQoNCi0gICBTdHJhaW46IHRoZSBuYW1lIG9mIHRoZSBzdHJhaW4gd2hpY2ggd2UnbGwgbmVlZCBmb3IgbWVyZ2luZyB3aXRoIG91ciB0cmVlIHN0cnVjdHVyZS4NCg0KTGV0J3MgcHVsbCB0aGF0IGluZm9ybWF0aW9uIGRvd24gYW5kIGFkZCBpdCB0byBvdXIgYHRibF90cmVlYCBiZWZvcmUgY29udmVydGluZyBpdCB0byBhIGB0cmVlZGF0YWAgb2JqZWN0Lg0KDQpgYGB7cn0NCiMgQWRkIHBoeWxvZ2VuZXRpYyBpbmZvcm1hdGlvbiB0byBvdXIgdHJlZQ0KU0MyX3ZhcmlhbnRzX3RpbWUudHJlZSA8LQ0KDQogICMgUGFzcyBhbG9uZyB0aGUgbWV0YWRhdGENCiAgU0MyX21ldGFkYXRhLmRmICU+JSANCiAgDQogICMgU2VsZWN0IGp1c3QgYSBoYW5kZnVsIG9mIGF0dHJpYnV0ZXMgdG8gZ28gdG8gdGhlIHRyZWUNCiAgc2VsZWN0KFN0cmFpbiwgJ0dJU0FJRCBjbGFkZScsICdFbWVyZ2luZyBOZXh0c3RyYWluIGNsYWRlJywgDQogICAgICAgICAnUEFOR08gbGluZWFnZScsIENvdW50cnksICdBZG1pbiBEaXZpc2lvbicsICdDb2xsZWN0aW9uIERhdGEnLCBBZ2UpICU+JSANCiAgDQogIHJlbmFtZShzdHJhaW4gPSBTdHJhaW4sDQogICAgICAgICBHSVNBSUQgPSAnR0lTQUlEIGNsYWRlJywgDQogICAgICAgICBlbWVyZ2luZ19uZXh0c3RyYWluID0gJ0VtZXJnaW5nIE5leHRzdHJhaW4gY2xhZGUnLA0KICAgICAgICAgUEFOR08gPSAnUEFOR08gbGluZWFnZScsDQogICAgICAgICBzdHJhaW5fY291bnRyeSA9IENvdW50cnksDQogICAgICAgICBzdHJhaW5fZGl2aXNpb24gPSAnQWRtaW4gRGl2aXNpb24nLCANCiAgICAgICAgIHN0cmFpbl9kYXRlID0gJ0NvbGxlY3Rpb24gRGF0YScpICU+JSANCiAgDQogICMgZnVsbF9qb2luIHRvIG91ciB0cmVlIHRvIGVuc3VyZSBubyBkYXRhIGlzIGxvc3QgYWx0aG91Z2ggeW91IGNvdWxkIGFsc28gY2FyZWZ1bGx5IHVzZSBhIGxlZnRfam9pbg0KICBmdWxsX2pvaW4oeD1TQzJfdmFyaWFudHNfdGltZS50YiwgeT0uLCBieT1jKCJsYWJlbCIgPSAic3RyYWluIikpICU+JSANCiAgDQogICMgQ29udmVydCB0byBhIHRpZHl0cmVlIGZvcm1hdA0KICBhcy50cmVlZGF0YSgpDQoNCiMgTG9vayBhdCB0aGUgcmVzdWx0aW5nIHN0cnVjdHVyZQ0Kc3RyKFNDMl92YXJpYW50c190aW1lLnRyZWUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjMuMS4xIEhvdyB0byBhY2Nlc3MgUzQgb2JqZWN0IHR5cGVzDQoNClRha2UgYSBxdWljayBsb29rIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhpcyB0cmVlZGF0YSBvYmplY3QuIFdoYXQgZG8gd2Ugc2VlPyBUaGUgYHRyZWVkYXRhYCBjbGFzcyBoYXMgY29udmVydGVkIG91ciB0aWJibGUgYmFjayBpbnRvIGFuICoqUzQqKiBvYmplY3Qgd2l0aCAxMSBzbG90cy4gRWFjaCBzbG90LCBpcyBlc3NlbnRpYWxseSBhIHBsYWNlaG9sZGVyIGZvciBhbm90aGVyIG9iamVjdC4gV2UgY2FuIGFjY2VzcyB0aGVzZSBzbG90cyB1c2luZyB0aGUgYEBgIG9wZXJhdG9yIGFuZCB3ZSBjYW4gZnVydGhlciBhY2Nlc3Mgc3ViIGVsZW1lbnRzIHdpdGggdGhlIGAkYCBvcGVyYXRvci4NCg0KRm9yIGluc3RhbmNlLCB3ZSBjYW4gc2VlIHRoZXJlIGlzIGEgYHBoeWxvYCBzbG90IHdoaWNoIGxvb2tzIHN1c3BpY2lvdXNseSBsaWtlIG91ciBvcmlnaW5hbCBgU0MyX3ZhcmlhbnRzX3RpbWUucGh5bG9gIG9iamVjdC4gVGhlIHJlc3Qgb2Ygb3VyIGRhdGEgZnJhbWUgaW5mb3JtYXRpb24sIHdoaWNoIHdlIGp1c3QgYWRkZWQgdG8gdGhlIGB0YmxfdHJlZWAgYmVmb3JlIGNvbnZlcnRpbmcgaXQgaXMgc3RvcmVkIGluIHRoZSBgZGF0YWAgc2xvdC4gVGhlcmUgYXJlIGFkZGl0aW9uYWwgc2xvdHMgd2hlcmUgd2UgY2FuIGFkZCBzZXF1ZW5jaW5nIGluZm9ybWF0aW9uIGZvciBjb21wYXJpc29uIGluIGRpZmZlcmVudCB0eXBlcyBvZiBwaHlsb2dlbmV0aWMgdHJlZSB2aXN1YWxpemF0aW9ucy4NCg0KYGBge3J9DQojIEdyYWIgb3VyIHBoeWxvIG9iamVjdA0KLi4uDQoNCiMgVGFrZSBhIHBlZWsgYXQgb3VyIGFzc29jaWF0ZWQgbm9kZSBkYXRhDQpoZWFkKC4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS40LjAgUGxvdCB5b3VyIHRyZWUgd2l0aCBgZ2d0cmVlKClgIGZyb20gdGhlIGBnZ3RyZWVgIHBhY2thZ2UNCg0KTm93IHRoYXQgd2UndmUgcHV0IHNvbWUgZXh0cmEgZGF0YSBpbnRvIG91ciB0cmVlLCB3ZSBhcmUgcmVhZHkgdG8gcGxvdCBpdCB3aXRoIHRoZSBoZWxwIG9mIHRoZSBgZ2d0cmVlKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBnZ3RyZWVgIHBhY2thZ2UuIElmIHlvdSBoYXZlbid0IG5vdGljZWQgeWV0LCBgdHJlZWlvYCwgYHRpZHl0cmVlYCBhbmQgYGdndHJlZWAgZm9ybSBhIHN1aXRlIG9mIHBhY2thZ2VzIHRoYXQgd2UgY2FuIHVzZSB0byBpbXBvcnQsIGFsdGVyLCBhbmQgdmlzdWFsaXplIG91ciB0cmVlcy4NCg0KUGFyYW1ldGVycyBmb3IgYGdndHJlZSgpYCBpbmNsdWRlOg0KDQotICAgYHRyYDogdGhlIHBoeWxvIHRyZWUgb2JqZWN0DQoNCi0gICBgbGF5b3V0YDogdGhlIHNoYXBlIG9mIHRoZSB0cmVlIGluY2x1ZGluZzogcmVjdGFuZ3VsYXIsIGRlbmRyb2dyYW0sIHNsYW50ZWQsIGVsbGlwc2UsIGZhbiwgY2lyY3VsYXIsIGlud2FyZF9jaXJjdWxhciwgYW5kIHJhZGlhbC4NCg0KLSAgIGBtcnNkYDogbW9zdCByZWNlbnQgc2FtcGxpbmcgZGF0ZSB1c2VkIGZvciBzZXR0aW5nIGEgdGltZS1iYXNlZCBzY2FsZQ0KDQotICAgYGFzLkRhdGVgOiBsb2dpY2FsIHRvIHNwZWNpZnkgaWYgZGF0ZSB3aWxsIGJlIGluIGNhbGVuZGFyIHZzIGRlY2ltYWwtZGF0ZSBmb3JtYXQNCg0KLSAgIGBicmFuY2gubGVuZ3RoYDogdmFyaWFibGUgZm9yIHNjYWxpbmcgYnJhbmNoIGxlbmd0aA0KDQotICAgYHJvb3QucG9zaXRpb25gOiB0aGUgcG9zaXRpb24gb2Ygb3VyIHJvb3Qgbm9kZSAoZGVmYXVsdCA9IDApDQoNCllvdSdsbCBub3RpY2UgdGhhdCBgZ2d0cmVlKClgIGFwcGVhcnMgdG8gYWN0IHZlcnkgbXVjaCBsaWtlIHRoZSBgZ2dwbG90KClgIGNvbW1hbmQuIFdlJ2xsIGJlIGFibGUgdG8gYWRkIGxheWVycyB0byB0aGUgYmFzZSB2aXN1YWxpemF0aW9uIG11Y2ggbGlrZSB3ZSBkbyB3aXRoIG90aGVyIGdncGxvdCBvYmplY3RzLiBXZSdsbCBhbHNvIHVzZSBgdGhlbWVfdHJlZTIoKWAgd2hpY2ggd2lsbCBhdXRvbWF0aWNhbGx5IGFkZCBhbiB4LWF4aXMgc2NhbGUgdG8gb3VyIHRyZWUuDQoNCldlJ2xsIHN0YXJ0IHNpbXBsZSBieSBqdXN0IHZpc3VhbGl6aW5nIGFsbCA2LDYyNyB0aXBzIG9mIG91ciB0cmVlLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KDQojIFNob3cgdGhlIGVudGlyZSB0cmVlDQoNClNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lIA0KDQojIDEuIERhdGENCmdndHJlZSh0ciA9IC4pICsgDQogICMgMi4gQWVzdGhldGljcw0KICBhZXMoY29sb3IgPSAuLi4pICsgDQogIA0KICAjIFRoZW1lcw0KICB0aGVtZV90cmVlMigpICsgDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsgIyBNb3ZlIG91ciBsZWdlbmQgdG8gdGhlIGJvdHRvbQ0KDQogICMgUHJvdmlkZSBzb21lIGxhYmVscw0KICBsYWJzKHg9IlRpbWUiLA0KICAgICAgIHRpdGxlID0gIkNhbmFkYSBhbmQgVVMgc2VxdWVuY2VkIHN0cmFpbiBwaHlsb2dlbnkiKSArDQoNCiAgIyBNYWtlIG91ciBndWlkZSBsaW5lcyBhIGxpdHRsZSB0aGlja2VyIA0KICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIk5leHRzdHJhaW5cbmNsYWRlIiwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0yKSkpICAgDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuNS4wIEZpbHRlciB5b3VyIHRyZWUgdXNpbmcgYGRyb3AudGlwKClgDQoNCkxvb2tpbmcgYXQgb3VyIHZpc3VhbGl6YXRpb24sIHRoZXJlIGFyZSBhIGxvdCBvZiB0aXBzIHRvIGRpc3BsYXkgYW5kIHdlJ3ZlIGNvbG91cmVkIHRoZSB0cmVlIGFsb25nIHRoZSBicmFuY2hlcy4gQW5vdGhlciB0aGluZyB3ZSBjYW4gc2VlIGlzIHRoYXQgdGhlcmUgaXMgYW4gIk5BIiB2YWx1ZS4gVGhpcyBtb3N0bHkgcmVwcmVzZW50cyB0aGUgYnJhbmNoZXMgb2YgdGhlIHRyZWUgY29ubmVjdGluZyBub2RlcyBpbnRlcm5hbGx5IHNpbmNlIHRoZXkgZG8gbm90IGhhdmUgYSBzcGVjaWZpYyBsaW5lYWdlIGFzc29jaWF0ZWQgZnJvbSBvdXIgZGF0YS4NCg0KQXMgaXQgc3RhbmRzLCB0aGVyZSdzIGp1c3Qgd2F5IHRvbyBtdWNoIGRhdGEgaGVyZSB0byBsb29rIGF0IGFuZCB3b3JrIHdpdGguIFdlIGNhbiB0cmltIHRoYXQgZGF0YSB1c2luZyB0aGUgYGRyb3AudGlwKClgIGZ1bmN0aW9uIHdpdGhvdXQgd29ycnlpbmcgYWJvdXQgaG93IHRoZSB0cmVlIGlzIHBhcnNlZC4gSW50ZXJuYWxseSwgYWxsIHRoYXQgcHJ1bmluZyB3aWxsIHRha2UgcGxhY2Ugd2l0aGluIHRoZSBmdW5jdGlvbi4gVG8gZG8gdGhpcywgd2UnbGwgZ2VuZXJhdGUgYSBsaXN0IG9mIHRpcHMgd2Ugd2FudCB0byAqcmVtb3ZlKiBmaXJzdC4NCg0KYGBge3J9DQojIFlvdSBjYW4gbWFrZSBhIGxpc3Qgb2YgdGlwcyB0byBkcm9wIHdoZW4geW91J3JlIG1ha2luZyB5b3VyIGdndHJlZQ0KDQojIEdlbmVyYXRlIGEgdGFibGUgb2YgdHJlZSBpbmZvcm1hdGlvbiB0byBkcm9wDQp0b19kcm9wIDwtDQoNCiAgU0MyX21ldGFkYXRhLmRmICU+JSANCiAgDQogICMgRmlsdGVyIGZvciBzdHJhaW5zIHRoYXQgYXJlIG5vdCBmcm9tIENhbmFkYSBhbmQgYXJlIGZyb20gYmVmb3JlIDIwMjAtMTEtMDENCiAgZmlsdGVyKCEoQ291bnRyeSA9PSAuLi4gJg0KICAgICAgICAgICBgQ29sbGVjdGlvbiBEYXRhYCA+PSAuLi4NCiAgICAgICAgICApKSAlPiUNCiAgDQogICMgSnVzdCBrZWVwIHRoZSBzdHJhaW4gaW5mb3JtYXRpb24NCiAgcHVsbChTdHJhaW4pDQoNCiMgVGFrZSBhIGxvb2sgYXQgd2hhdCB3ZSBnb3QgYmFjaw0Kc3RyKHRvX2Ryb3ApDQpgYGANCg0KYGBge3J9DQojIEF0IHRoZSBzYW1lIHRpbWUgbWFrZSBhIGRhdGFzZXQgdG8ga2VlcC4gWW91J2xsIHVzZSB0aGlzIHRvIHVwZGF0ZSB0aGUgaW5mb3JtYXRpb24gaW4gdGhlIHRyZWUgbGF0ZXINCnRvX2tlZXAgPC0gU0MyX21ldGFkYXRhLmRmICU+JSBmaWx0ZXIoLi4uKQ0KDQojIFdoaWNoIHRpcHMgYXJlIHdlIGtlZXBpbmc/DQpzdHIodG9fa2VlcCwgZ2l2ZS5hdHRyID0gRkFMU0UpICAgICAgICAgDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuNi4wIFVwZGF0ZSBvdXIgcHJ1bmVkIHRyZWUgYW5kIGFkZCBzb21lIGV4dHJhIGBnZW9tXypgIGxheWVycw0KDQpUaGUgYGdndHJlZWAgcGFja2FnZSBicmluZ3MgYSBudW1iZXIgb2YgYGdncGxvdDJgLWNvbXBhdGlibGUgZ2VvbXMgdG8gb3VyIGZpbmdlci0qdGlwcyouIFdlJ2xsIHNwcnVjZSB1cCBvdXIgdHJlZSB3aXRoIHNvbWUgdGlwIGxhYmVscyB0byBiZWdpbiB3aXRoLiBXZSdsbCBhY2NvbXBsaXNoIHRoaXMgd2l0aCB0aGUgYGdlb21fdGlwbGFiKClgIGxheWVyLg0KDQp8IENvbXBvbmVudCB0eXBlIHwgTGF5ZXIgdHlwZSB8IENvbW1hbmQgICAgICAgICB8IERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgICAgICBUcmVlICAgICAgfCBHZW9tICAgICAgIHwgZ2VvbV90cmVlICAgICAgIHwgdHJlZSBzdHJ1Y3R1cmUgbGF5ZXIsIHdpdGggbXVsdGlwbGUgbGF5b3V0IHN1cHBvcnRlZCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgIFRyZWUgICAgICB8IEdlb20gICAgICAgfCBnZW9tX3RyZWVzY2FsZSAgfCB0cmVlIGJyYW5jaCBzY2FsZSBsZWdlbmQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgTm9kZSAgICAgIHwgR2VvbSAgICAgICB8IGdlb21fbm9kZXBvaW50ICB8IGFubm90YXRlIGludGVybmFsIG5vZGVzIHdpdGggc3ltYm9saWMgcG9pbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICBOb2RlICAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV9yYW5nZSAgICAgIHwgYmFyIGxheWVyIHRvIHByZXNlbnQgdW5jZXJ0YWludHkgb2YgZXZvbHV0aW9uYXJ5IGluZmVyZW5jZSAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgIE5vZGUgICAgICB8IEFubm90YXRpb24gfCBnZW9tX3Jvb3Rwb2ludCAgfCBhbm5vdGF0ZSByb290IG5vZGUgd2l0aCBzeW1ib2xpYyBwb2ludCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICBCcmFuY2ggICAgIHwgQW5ub3RhdGlvbiB8IGdlb21fbGFiZWwyICAgICB8IG1vZGlmaWVkIHZlcnNpb24gb2YgZ2VvbV9sYWJlbCwgd2l0aCBzdWJzZXR0aW5nIHN1cHBvcnRlZCBmb3IgbGFiZWxsaW5nIGJyYW5jaGVzIHwNCnwgICAgIEJyYW5jaCAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV9zZWdtZW50MiAgIHwgbW9kaWZpZWQgdmVyc2lvbiBvZiBnZW9tX3NlZ21lbnQsIHdpdGggc3Vic2V0dGluZyBzdXBwb3J0ZWQgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgIFRheGEgICAgICB8IEdlb20gICAgICAgfCBnZW9tX3BvaW50MiAgICAgfCBtb2RpZmllZCB2ZXJzaW9uIG9mIGdlb21fcG9pbnQsIHdpdGggc3Vic2V0dGluZyBzdXBwb3J0ZWQgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgVGF4YSAgICAgIHwgR2VvbSAgICAgICB8IGdlb21fdGlwcG9pbnQgICB8IGFubm90YXRlIGV4dGVybmFsIG5vZGVzIHdpdGggc3ltYm9saWMgcG9pbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICBUYXhhICAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV90YXhhbGluayAgIHwgYXNzb2NpYXRlIHR3byByZWxhdGVkIHRheGEgYnkgbGlua2luZyB0aGVtIHdpdGggYSBjdXJ2ZSAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgIFRheGEgICAgICB8IEFubm90YXRpb24gfCBnZW9tX3RleHQyICAgICAgfCBtb2RpZmllZCB2ZXJzaW9uIG9mIGdlb21fdGV4dCwgd2l0aCBzdWJzZXR0aW5nIHN1cHBvcnRlZCAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgVGF4YSAgICAgIHwgQW5ub3RhdGlvbiB8IGdlb21fdGlwbGFiICAgICB8IGxheWVyIG9mIHRpcCBsYWJlbHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICBUYXhhICAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV90aXBsYWIyICAgIHwgbGF5ZXIgb2YgdGlwIGxhYmVscyBmb3IgY2lyY3VsYXIgbGF5b3V0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgQ2xhZGUgICAgICB8IEFubm90YXRpb24gfCBnZW9tX2JhbGFuY2UgICAgfCBoaWdobGlnaHRzIHRoZSB0d28gZGlyZWN0IGRlc2NlbmRhbnQgY2xhZGVzIG9mIGFuIGludGVybmFsIG5vZGUgICAgICAgICAgICAgICAgICB8DQp8ICAgICBDbGFkZSAgICAgIHwgQW5ub3RhdGlvbiB8IGdlb21fY2xhZGVsYWJlbCB8IGFubm90YXRlIGEgY2xhZGUgd2l0aCBiYXIgYW5kIHRleHQgbGFiZWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgIENsYWRlICAgICAgfCBBbm5vdGF0aW9uIHwgZ2VvbV9oaWxpZ2h0ICAgIHwgaGlnaGxpZ2h0IGEgY2xhZGUgd2l0aCByZWN0YW5nbGUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgQ2xhZGUgICAgICB8IEFubm90YXRpb24gfCBnZW9tX3N0cmlwICAgICAgfCBhbm5vdGF0ZSBhc3NvY2lhdGVkIHRheGEgd2l0aCBiYXIgYW5kIChvcHRpb25hbCkgdGV4dCBsYWJlbCAgICAgICAgICAgICAgICAgICAgICB8DQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQojIFNob3cgb3VyIHBydW5lZCB0cmVlDQoNClNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogICMgZHJvcCB0aXBzIGJhc2VkIG9uIG91ciBsaXN0DQogIGRyb3AudGlwKC4sIHRvX2Ryb3ApICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2d0cmVlKHRyID0gLikgKyAjIE5lZWQgdGhlIG1vc3QgcmVjZW50IHNhbXBsaW5nIGRhdGUNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoY29sb3IgPSBlbWVyZ2luZ19uZXh0c3RyYWluKSArIA0KICAgIA0KICAgICMgVGhlbWVzDQogICAgdGhlbWVfdHJlZTIoKSArIA0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKyAjIE1vdmUgb3VyIGxlZ2VuZCB0byB0aGUgYm90dG9tDQogIA0KICAgICMgUHJvdmlkZSBzb21lIGxhYmVscw0KICAgIGxhYnMoeD0iVGltZSIsDQogICAgICAgICB0aXRsZSA9ICJTdWJzZXQgb2YgQ2FuYWRpYW4gc2VxdWVuY2VkIHN0cmFpbnMiKSArDQogICAgDQogICAgIyBNYWtlIG91ciBndWlkZSBsaW5lcyBhIGxpdHRsZSB0aGlja2VyIA0KICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiTmV4dHN0cmFpblxuY2xhZGUiLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MikpKSArICAgIA0KICAgIA0KICAgICMgNC4gR2VvbXMNCiAgICAjIyMgMS42LjAgQWRkIHRpcHMgbGFiZWxzIHRvIG91ciB0cmVlIHRpcHMNCiAgICAuLi4gICAgDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjYuMSBSZXBsYWNlIGxhYmVscyB3aXRoIHBvaW50cyB1c2luZyBkaWZmZXJlbnQgZ2VvbXMNCg0KSW4gYSB0cmVlIHRoaXMgcGFja2VkLCB5b3UgY2FuJ3QgcmVhbGx5IHJlYWQgdGhlIHRpcCBsYWJlbHMuIFRoZSBkYXRhIHRoYXQncyBtb3N0IGhlbHBmdWwsIGhvd2V2ZXIsIGlzIHRoZSBjb2xvdXJpbmcuIFlvdSBjYW4gcmVwbGFjZSB5b3VyIGxhYmVscyB3aXRoIHBvaW50cyBpbiAzIHdheXM6DQoNCi0gICBgZ2VvbV9wb2ludCgpYA0KDQotICAgYGdlb21fbm9kZXBvaW50KClgDQoNCi0gICBgZ2VvbV90aXBwb2ludCgpYA0KDQpJZiB3ZSB1c2UgYGdlb21fcG9pbnQoKWAgd2UgY2FuIGNvbG91ci9hbm5vdGF0ZSBhbGwgb2YgdGhlIG5vZGVzIGFuZCB0aXBzIGJ1dCB3ZSBhbHJlYWR5IGtub3cgdGhhdCB0aGVyZSBpc24ndCBhbnkgcGh5bG9nZW5ldGljIGluZm9ybWF0aW9uIGFzc29jaWF0ZWQgd2l0aCBvdXIgaW50ZXJuYWwgbm9kZXMuIExpa2V3aXNlLCBgZ2VvbV9ub2RlcG9pbnQoKWAgd2lsbCBhbGxvdyB1cyB0byBjb2xvdXIgdGhlIG5vZGVzIGJ1dCB0aGVyZSBpc24ndCBhbnkgZ3JvdXBpbmcgaW5mb3JtYXRpb24gYXNzb2NpYXRlZCB3aXRoIHRoYXQuDQoNClRoZXJlZm9yZSB3ZSdsbCB1c2UgYGdlb21fdGlwcG9pbnQoKWAgdG8gY29sb3VyIG91ciB0aXBzIGJhc2VkIG9uIHRoZWlyIE5leHRzdHJhaW4gY2xhZGUgaW5mb3JtYXRpb24gYW5kIHdlIGNhbiBhbHRlciB0aGUgdGlwIHNoYXBlIHRvIG1hdGNoIHRoZSBwcm92aW5jZSB3aGVyZSB0aGUgc3RyYWluIHdhcyBzZXF1ZW5jZWQuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQojIFNob3cgZW50aXJlIHRyZWUNCg0KU0MyX3ZhcmlhbnRzX3RpbWUudHJlZSAlPiUNCiAgZHJvcC50aXAoLiwgdG9fZHJvcCkgJT4lIA0KDQogICMgMS4gRGF0YQ0KICBnZ3RyZWUodHIgPSAuKSArICMgTmVlZCB0aGUgbW9zdCByZWNlbnQgc2FtcGxpbmcgZGF0ZQ0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyhjb2xvciA9IGVtZXJnaW5nX25leHRzdHJhaW4pICsgDQogICAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZV90cmVlMigpICsgDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAjIE1vdmUgb3VyIGxlZ2VuZCB0byB0aGUgYm90dG9tDQogICAgICAgICAgbGVnZW5kLmJveCA9ICJ2ZXJ0aWNhbCIpICsgICMgU3RhY2sgbGVnZW5kcyB2ZXJ0aWNhbGx5DQogIA0KICAgICMgUHJvdmlkZSBzb21lIGxhYmVscw0KICAgIGxhYnMoeD0iVGltZSIsDQogICAgICAgICB0aXRsZSA9ICJTdWJzZXQgb2YgQ2FuYWRpYW4gc2VxdWVuY2VkIHN0cmFpbnMiKSArDQogICAgDQogICAgIyBNYWtlIG91ciBndWlkZSBsaW5lcyBhIGxpdHRsZSB0aGlja2VyIA0KICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiTmV4dHN0cmFpblxuY2xhZGUiLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MikpLA0KICAgICAgICAgICBzaGFwZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJTdHJhaW5cbmxvY2F0aW9uIikNCiAgICAgICAgICApICsgDQogIA0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE1OjIwLCAxMSkpICsNCiAgICANCiAgICAjIDQuIEdlb21zDQogICAgIyBBZGQgdGlwcyBsYWJlbHMgdG8gb3VyIHRyZWUgdGlwcw0KICAgICMjIyAxLjYuMSBDaGFuZ2Ugb3VyIHRpcCBzaGFwZSBieSBzdHJhaW5fZGl2aXNpb24NCiAgICAuLi4gICMgQWRkIHRpcHMgb25seSAgICANCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuNi4yIEZpeCB5b3VyIHgtYXhpcyB3aXRoIHRoZSBgbXJzZGAgcGFyYW1ldGVyDQoNCkFzIHlvdSBjYW4gc2VlIHdlIGFyZSB3b3JraW5nIHdpdGggYSB0aW1lLWJhc2VkIGF4aXMgYnV0IGl0IGFsbCBhcHBlYXJzIHRvIGJlIG9uIGEgcmVsYXRpdmUgc2NhbGUgYW5kIHdoYXQgd2UnZCByZWFsbHkgbGlrZSB0byBzZWUgaXMgcmVhbC13b3JsZCB0aW1lLiBJbiBvcmRlciB0byBkbyB0aGF0LCB3ZSBjYW4gYXNzaWduIHRoZSBgbXJzZGAgcGFyYW1ldGVyIHRvIHRoZSBtb3N0IHJlY2VudCBzYW1wbGluZyBkYXRlIGluIG91ciBkYXRhc2V0LiBXZSdsbCBhbHNvIHNldCBvdXIgcGFyYW1ldGVyIGBhcy5EYXRlYCBpbiBvcmRlciB0byBzZWUgdGhlIGRhdGUgaW4gYSBjYWxlbmRhciBmb3JtYXQgcmF0aGVyIHRoYW4gYSBkZWNpbWFsLWRhdGUgZm9ybWF0Lg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KIyBTaG93IGVudGlyZSB0cmVlDQoNClNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogIGRyb3AudGlwKC4sIHRvX2Ryb3ApICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3RyZWUodHIgPSAuLCANCiAgICAgICBtcnNkID0gLi4uLCAgICAjIyMgMS42LjIgU2V0IHRoZSBtb3N0IHJlY2VudCBzYW1wbGluZyBkYXRlDQogICAgICAgYXMuRGF0ZSA9IC4uLikgKyAgICAgICAjIyMgMS42LjIgTWFrZSBzdXJlIGl0J3Mgc2V0IGFzIGEgZGF0YSANCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoY29sb3IgPSBlbWVyZ2luZ19uZXh0c3RyYWluKSArIA0KICAgIA0KICAgICMgVGhlbWVzDQogICAgdGhlbWVfdHJlZTIoKSArIA0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgIyBNb3ZlIG91ciBsZWdlbmQgdG8gdGhlIGJvdHRvbQ0KICAgICAgICAgIGxlZ2VuZC5ib3ggPSAidmVydGljYWwiKSArICAjIFN0YWNrIGxlZ2VuZHMgdmVydGljYWxseQ0KDQogICAgIyBQcm92aWRlIHNvbWUgbGFiZWxzDQogICAgbGFicyh4PSJUaW1lIiwNCiAgICAgICAgIHRpdGxlID0gIlN1YnNldCBvZiBDYW5hZGlhbiBzZXF1ZW5jZWQgc3RyYWlucyIpICsNCiAgICANCiAgICAjIE1ha2Ugb3VyIGd1aWRlIGxpbmVzIGEgbGl0dGxlIHRoaWNrZXIgDQogICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJOZXh0c3RyYWluXG5jbGFkZSIsICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0yKSksDQogICAgICAgICAgIHNoYXBlID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIlN0cmFpblxubG9jYXRpb24iKQ0KICAgICAgICAgICkgKyANCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE1OjIwLCAxMSkpICsNCg0KICAgICMgNC4gR2VvbXMNCiAgICAjIEFkZCB0aXBzIGxhYmVscyB0byBvdXIgdHJlZSB0aXBzDQogICAgIyBDaGFuZ2Ugb3VyIHRpcCBzaGFwZSBieSBzdHJhaW5fZGl2aXNpb24NCiAgICBnZW9tX3RpcHBvaW50KGFlcyhzaGFwZSA9IHN0cmFpbl9kaXZpc2lvbiksIGFscGhhID0gMC43LCBzaXplID0gMykgICMgQWRkIHRpcHMgb25seSAgICANCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS43LjAgVGhpbiBvdXQgdGhlIHRyZWUgc29tZSBtb3JlIHdpdGggYHZpZXdDbGFkZSgpYCBhbmQgb3RoZXIgaW5mb3JtYXRpb24gZnVuY3Rpb25zDQoNCkxvb2tpbmcgYXQgdGhlIHRyZWUgdGhlcmUgYXJlIHN0aWxsIHF1aXRlIGEgZmV3IG5vZGVzIGJ1dCB3ZSBjYW4gem9vbSBpbiBvbiBhIHNwZWNpZmljIGNsYWRlIHVzaW5nIHRoZSBgdmlld0NsYWRlKClgIGZ1bmN0aW9uLiBUbyBhY2NvbXBsaXNoIHRoaXMgd2UgbmVlZCB0byBhY2Nlc3MgdGhlIG1vc3QgcmVjZW50IGNvbW1vbiBhbmNlc3RvciAoTVJDQSkgb2YgYSBncm91cC4gQXQgdGhlIHNhbWUgdGltZSwgd2Ugc2hvdWxkIHRhbGsgYWJvdXQgd2F5cyB0byB0cmF2ZXJzZSBvciBuYXZpZ2F0ZSB0aGlzIHRyZWUuIEhlcmUncyB3aGVyZSBgdGlkeXRyZWVgIG9mZmVycyB1cCBhIGZldyBhZGRpdGlvbmFsIGZ1bmN0aW9ucyBmb3Igc2VhcmNoaW5nIHlvdXIgYHRibF90cmVlYC4gVGhlc2UgdGFrZSBvbiB0aGUgZm9ybSBvZiBgZnVuY3Rpb24odGJsX3RyZWUsIG5vZGVfbnVtYmVyKWAgb3IgYGZ1bmN0aW9uKHRibF90cmVlLCBub2RlX2xhYmVsKWANCg0KfCBmdW5jdGlvbiAgICB8IERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBjaGlsZCgpICAgICB8IEZpbmQgdGhlIGNoaWxkcmVuIG9mIGFuIGludGVybmFsIG5vZGUuICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgcGFyZW50KCkgICAgfCBGaW5kIHRoZSBwYXJlbnQgbm9kZSBvZiBhIHRpcCBvciBvdGhlciBub2RlLiAgICAgICAgICAgICAgICAgICB8DQp8IG9mZnNwcmluZygpIHwgRmluZCBhbGwgb2YgdGhlIG9mZnNwcmluZyBvZiBhIG5vZGUuICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBhbmNlc3RvcigpICB8IEZpbmQgYWxsIG9mIHRoZSBhbmNlc3RvcnMgb2YgYSB0aXAgb3Igbm9kZSAgICAgICAgICAgICAgICAgICAgIHwNCnwgc2libGluZygpICAgfCBGaW5kIHRoZSBuZWFyZXN0IHNpYmxpbmcgb2YgYSB0aXAgKG9yIG5vZGUpICAgICAgICAgICAgICAgICAgICB8DQp8IE1SQ0EoKSAgICAgIHwgRmluZCB0aGUgbW9zdCByZWNlbnQgY29tbW9uIGFuY2VzdG9yIGJldHdlZW4gdHdvIG9yIG1vcmUgbm9kZXMgfA0KDQpMZXQncyBmaWx0ZXIgb3VyIHRheGEgbGlzdCBhIGxpdHRsZSBtb3JlIHNlYXJjaGluZyBmb3IgdmFyaWFudHMgb2YgdGhlIGNsYWRlIDUwMVkuVjEgYW5kIDUwMVkuVjIuIFRoZW4gd2UnbGwgZmluZCB0aGUgTVJDQSBiZXR3ZWVuIHRob3NlIHRpcHMuDQoNCmBgYHtyfQ0KIyBZb3UgY2FuIG1ha2UgYSBsaXN0IG9mIHRpcHMgdG8gZHJvcCB3aGVuIHlvdSdyZSBtYWtpbmcgeW91ciBnZ3RyZWUNCg0KIyBHZW5lcmF0ZSBhIHRhYmxlIG9mIHRyZWUgaW5mb3JtYXRpb24gdG8gZHJvcA0KdG9fdmlldyA8LQ0KDQogIFNDMl9tZXRhZGF0YS5kZiAlPiUgDQogIA0KICAgICMgRmlsdGVyIG91ciB0aXAgaW5mb3JtYXRpb24NCiAgICBmaWx0ZXIoYEVtZXJnaW5nIE5leHRzdHJhaW4gY2xhZGVgICVpbiUgYygiMjBJLzUwMVkuVjEiLCAiMjBILzUwMVkuVjIiKSAmIA0KICAgICAgICAgICBDb3VudHJ5ID09IC4uLiAmDQogICAgICAgICAgIGBDb2xsZWN0aW9uIERhdGFgID49IGFzLkRhdGUoIjIwMjEtMDItMTYiKQ0KICAgICAgICAgICkgJT4lIA0KICANCiAgICAjIEp1c3Qga2VlcCB0aGUgc3RyYWluIG5hbWVzDQogICAgcHVsbChTdHJhaW4pDQoNCiMgV2hhdCdzIHRoZSBsaXN0IGxvb2sgbGlrZT8NCnRvX3ZpZXcNCmBgYA0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBNUkNBIGZyb20gYSBzdWJzZXQgb2Ygb3VyIHRpcHMNCg0KdG9fdmlld19NUkNBIDwtDQogICMgUGFzcyBhbG9uZyBvdXIgdHJlZSBpbmZvcm1hdGlvbg0KICBTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JQ0KICANCiAgIyBHZXQgcmlkIG9mIHRoZSB0aXBzIHdlIHdvdWxkIGJlZm9yZSAodG8gbWF0Y2ggdGhlIHRyZWUgd2UnbGwgcGxvdCkNCiAgZHJvcC50aXAoLiwgdG9fZHJvcCkgJT4lDQogIA0KICAjIERldGVybWluZSB0aGUgTVJDQSBiZXR3ZWVuIHRoZSBmaXJzdCA0IHRpcHMgaW4gdGhlIGxpc3QNCiAgTVJDQSguLCAuLi4pDQoNCiMgV2hhdCBraW5kIG9mIHZhbHVlIGRvZXMgTVJDQSgpIHJldHVybj8NCnRvX3ZpZXdfTVJDQQ0KYGBgDQoNCmBgYHtyfQ0KIyBIb3cgbWFueSBvZmZzcHJpbmcgZG9lcyBpdCBoYXZlPw0KDQojIFBhc3MgYWxvbmcgb3VyIHRyZWUgaW5mb3JtYXRpb24NClNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogIA0KICAjIERyb3Agb3VyIG9yaWdpbmFsIHNldCBvZiB0aXBzDQogIGRyb3AudGlwKC4sIHRvX2Ryb3ApICU+JSANCiAgIyBtYWtlIGEgdGliYmxlIHNvIHdlIGNhbiBzZWUgYWxsIHRoZSByZWxldmFudCBpbmZvcm1hdGlvbg0KICBhc190aWJibGUoKSAlPiUgDQogICMgRmluZCB0aGUgb2Zmc3ByaW5nIG9mIGEgc3BlY2lmaWMgbm9kZQ0KICBvZmZzcHJpbmcoLiwgLi4uKSAlPiUgDQogICMgQ29udmVydCB0byBhIHRyZWUNCiAgYXMudHJlZWRhdGEoKSAlPiUgDQogIHN0cigpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjcuMSBQYXNzIGEgYGdncGxvdDJgIG9iamVjdCB0byBgdmlld0NsYWRlKClgDQoNCldlIHNlZSB0aGF0IG91ciBjaG9zZW4gTVJDQSBub2RlICg3MjcpIGhhcyAzOSBjaGlsZHJlbiBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIHRpcCBsYWJlbHMgaW4gb3VyIGB0cmVlZGF0YWAgb2JqZWN0LiBXZSBjYW4gYWxzbyBub3cgdXNlIHRoaXMgTVJDQSB0byBkZXRlcm1pbmUgdGhlIHNwZWNpZmljIGNsYWRlIHdlIGNhbiBzZWUgb24gdGhlIHRyZWUgYnV0IHRoaXMgd2lsbCBzdGlsbCBiZSBxdWl0ZSBsYXJnZS4gVG8gdmlldyB0aGlzIHBhcnRpY3VsYXIgY2xhZGUsIHdlIGJ1aWxkIG91ciBwbG90IGFzIGJlZm9yZSwgYW5kIHRoZW4gem9vbSBpbnRvIGl0IGFmdGVyd2FyZHMgd2l0aCB0aGUgYHZpZXdDbGFkZSgpYCBmdW5jdGlvbi4NCg0KVW5mb3J0dW5hdGVseSwgd2UnbGwgaGF2ZSB0byBkaXRjaCBvdXIgZGF0ZSBzY2FsZSBhbmQgcmV2ZXJ0IHRvIGEgZGVjaW1hbC1kYXRlLiBJZiB5b3UncmUgbm90IHVzaW5nIGEgY2FsZW5kYXItYmFzZWQgZGF0ZSwgaXQncyBub3QgYSBwcm9ibGVtIGF0IGFsbC4gSWYgd2UgbG9vayBjYXJlZnVsbHkgYXQgdGhlIGRhdGEsIHdlJ2xsIHNlZSB0aGF0IHRoZSBpbnRlcm5hbCBub2RlcyBoYXZlIGFuIE5BLXZhbHVlLiBJZiB5b3Ugd2VyZSAqaW5kdXN0cmlvdXMgZW5vdWdoKiwgeW91IGNvdWxkIHVzZSB0aGUgYnJhbmNoIGxlbmd0aHMgdG8gdHJhdmVyc2UgdGhlIHRyZWUgYW5kIGNhbGN1bGF0ZSBkYXRlcyBmb3IgYWxsIHRoZSBpbnRlcm5hbCBub2RlcyBhcyB3ZWxsLiBUaGlzIHdvdWxkIGxpa2VseSBzb2x2ZSB0aGUgaXNzdWUuDQoNCkxldCdzIGRyb3AgdGhlIHRpcCBuYW1lcyBiYWNrIGluIHRoZXJlIHRvby4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCiMgU2hvdyBhIHNtYWxsIGNsYWRlIG9mIHRoZSB0cmVlDQoNCnRyZWUucGxvdCA8LQ0KDQpTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JQ0KICAjIEZyb20gb3VyIHRyZWUsIGRyb3AgdGhlIE5vbi1DYW5hZGlhbiB0aXBzDQogIGRyb3AudGlwKC4sIHRvX2Ryb3ApICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3RyZWUodHIgPSAuLCBtcnNkID0gIjIwMjEtMDItMTYiKSArICMgTmVlZCB0aGUgbW9zdCByZWNlbnQgc2FtcGxpbmcgZGF0ZQ0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyhjb2xvciA9IGVtZXJnaW5nX25leHRzdHJhaW4pICsgDQogICAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZV90cmVlMigpICsgDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzApLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAjIE1vdmUgb3VyIGxlZ2VuZCB0byB0aGUgYm90dG9tDQogICAgICAgICAgbGVnZW5kLmJveCA9ICJ2ZXJ0aWNhbCIpICsgICMgU3RhY2sgbGVnZW5kcyB2ZXJ0aWNhbGx5DQoNCiAgICAjIFByb3ZpZGUgc29tZSBsYWJlbHMNCiAgICBsYWJzKHg9IlRpbWUiLA0KICAgICAgICAgdGl0bGUgPSAiU3Vic2V0IG9mIENhbmFkaWFuIHNlcXVlbmNlZCBzdHJhaW5zIikgKw0KICAgIA0KICAgICMgTWFrZSBvdXIgZ3VpZGUgbGluZXMgYSBsaXR0bGUgdGhpY2tlciANCiAgICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIk5leHRzdHJhaW5cbmNsYWRlIiwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTIpKSwNCiAgICAgICAgICAgc2hhcGUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiU3RyYWluXG5sb2NhdGlvbiIpDQogICAgICAgICAgKSArIA0KDQogICAgIyAzLiBTY2FsaW5nDQogICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTU6MjAsIDExKSkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwbGFiKHNpemUgPSA3LCBhbGlnbj1UUlVFKSArICMgQWRkIGxhYmVscyBpbiBhbmQgcmlnaHQtYWxpZ24gdGhlbSANCg0KICAgICMgQWRkIHRpcHMgbGFiZWxzIHRvIG91ciB0cmVlIHRpcHMNCiAgICAjIENoYW5nZSBvdXIgdGlwIHNoYXBlIGJ5IHN0cmFpbl9kaXZpc2lvbg0KICAgIGdlb21fdGlwcG9pbnQoYWVzKHNoYXBlID0gc3RyYWluX2RpdmlzaW9uKSwgc2l6ZSA9IDMpICAjIEFkZCB0aXBzIG9ubHkgICANCg0KIyMjIDEuNy4xIFZpZXcgdGhlIGNsYWRlIHdlIG1hZGUgdXNpbmcgdGhlIHNhbWUgTVJDQSBjYWxsIHdlIGFscmVhZHkgdXNlZC4NCnZpZXdDbGFkZSh0cmVlLnBsb3QsIC4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS44LjAgTWFrZSBhIG5ldyBzdWJwbG90IHdpdGggaW5mb3JtYXRpb24gZnJvbSBhY3Jvc3MgYWxsIGNsYWRlcw0KDQpTbyB3ZSd2ZSBsb29rZWQgYXQgYSBmZXcgd2F5cyB0byBzdWJzZXQgYW5kIHBsb3Qgb3VyIHBoeWxvZ2VuZXRpYyB0cmVlLiBXZSdsbCBkbyBhIHF1aWNrIGFzaWRlIHRvIGNyZWF0ZSBhIG1vcmUgY3VyYXRlZCBsaXN0IG9mIHRpcHMgd2l0aCByZXByZXNlbnRhdGlvbiBmcm9tIGFjcm9zcyBtdWx0aXBsZSBjbGFkZXMuIFdlJ2xsIHVzZSB0aGlzIGFzIHRoZSBiYXNlIHRyZWUgZm9yIG91ciBuZXh0IGZldyBncmFwaHMgc28gdGhhdCB3ZSBjYW4gcmVhbGx5IHNlZSB0aGUgcG93ZXIgb2YgdXNpbmcgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBmcm9tIG91ciBgdHJlZWRhdGFgIG9iamVjdC4NCg0KYGBge3J9DQojIEhlcmUncyBvdXIgcmVwcmVzZW50YXRpdmUgbGlzdCBvZiB0aXBzIGZyb20gbXVsdGlwbGUgY2xhZGVzDQoNCmN1cmF0ZWRfdGlwcyA8LQ0KYygnV3VoYW4vV0gwMS8yMDE5JywNCidDYW5hZGEvT05fT04tVklETy0wMS0yLzIwMjAnLA0KJ0NhbmFkYS9PTl9WSURPLTAxLzIwMjAnLA0KJ0NhbmFkYS9CQ18zN18wLTIvMjAyMCcsDQonQ2FuYWRhL0JDXzY5MjQzLzIwMjAnLA0KJ0NhbmFkYS9RQy1DSFVNLTIwMDgwMDM5NTZBLzIwMjAnLA0KJ0NhbmFkYS9OTC1OTUwtMTM4Ny8yMDIwJywNCidDYW5hZGEvTUItTk1MLTgwOC8yMDIwJywNCidDYW5hZGEvTkItTk1MLTE2OTY3LzIwMjEnLA0KJ0NhbmFkYS9CQ182MTI5MTI3LzIwMjAnLA0KJ0NhbmFkYS9BQi05Nzc3Ni8yMDIwJywNCidDYW5hZGEvQUItMTI5NDgvMjAyMCcsDQonQ2FuYWRhL0JDLUJDQ0RDLTM1NjQvMjAyMCcsDQonQ2FuYWRhL09OLVM2Ny8yMDIwJywNCidDYW5hZGEvQUItNjUyMzMvMjAyMCcsDQonQ2FuYWRhL01CLU5NTC0xMDU3LzIwMjAnLA0KJ0NhbmFkYS9PTi1VSFRDLTAzNjYvMjAyMCcsDQonQ2FuYWRhL05TLU5NTC0xNjIxOC8yMDIxJywNCidDYW5hZGEvTkItTk1MLTE2OTY2LzIwMjEnLA0KJ0NhbmFkYS9OU18xMy8yMDIwJywNCidDYW5hZGEvUWMtQ0hVTS0yMDE5MjAzODU2QS8yMDIwJywNCidDYW5hZGEvTkItTk1MLTMyNzIvMjAyMCcsDQonQ2FuYWRhL09OLVVIVEMtMDI2Ny8yMDIwJywNCidDYW5hZGEvTUItTk1MLTEwMzcvMjAyMCcsDQonQ2FuYWRhL05TLU5NTC01MjEzLzIwMjAnLA0KJ0NhbmFkYS9OUy1OTUwtNTE0MS8yMDIxJywNCidDYW5hZGEvTlMtTk1MLTUxNDAvMjAyMScsDQonQ2FuYWRhL05TLU5NTC0xNjIwOC8yMDIxJywNCidDYW5hZGEvQkMtQkNDREMtNzAxMi8yMDIwJywNCidDYW5hZGEvT04tTFRSSS0xMzcyLzIwMjAnLA0KJ0NhbmFkYS9CQy1CQ0NEQy05NzM2LzIwMjEnLA0KJ0NhbmFkYS9PTi1TMjEyNS8yMDIxJywNCidDYW5hZGEvTlMtTk1MLTUxODEvMjAyMCcsDQonQ2FuYWRhL09OLVBQUy0yMTAxMjAyMV8wMjY5LzIwMjEnLA0KJ0NhbmFkYS9RQy1MMDAzMTQ1MzkvMjAyMCcsDQonQ2FuYWRhL05TLU5NTC0xNjIzOC8yMDIxJywNCidDYW5hZGEvTkwtTk1MLTE2ODIyLzIwMjEnLA0KJ0NhbmFkYS9RQy1MMDAzMjQ1ODkwMDEvMjAyMScsDQonQ2FuYWRhL05MLU5NTC0xNzE5NC8yMDIxJykNCmBgYA0KDQpgYGB7cn0NCiMgTWFrZSBhIG5ldyBsaXN0IG9mIHRpcHMgdG8gZHJvcA0KY3VyYXRlZF9kcm9wX2xpc3QgPC0NCiAgU0MyX21ldGFkYXRhLmRmICU+JSANCg0KICAjIGZpbHRlciB0aGUgb3Bwb3NpdGUgb2Ygb3VyIHRpcCBsYWJlbHMNCiAgZmlsdGVyKCEoU3RyYWluICVpbiUgLi4uKSkgJT4lIA0KICAjIE9ubHkga2VlcCB0aGUgc3RyYWluIGluZm9ybWF0aW9uIGZvciB3aGF0IHdlIHdhbnQgdG8gZHJvcA0KICBwdWxsKFN0cmFpbikNCmBgYA0KDQpgYGB7cn0NCiMgRHJvcCB0aXBzIGZyb20gb3VyIHRyZWUgYW5kIHNhdmUgaXQgYXMgY3VyYXRlZF90cmVlX2luZm8uZGYNCmN1cmF0ZWRfdHJlZV9pbmZvLmRmIDwtDQoNCiAgIyBQYXNzIGFsb25nIG91ciBvcmlnaW5hbCB0cmVlDQogIFNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQoNCiAgIyBEcm9wIHRoZSB0aXBzIHdlIGNyZWF0ZWQNCiAgZHJvcC50aXAoLiwgLi4uKSAlPiUgDQoNCiAgIyBDb252ZXJ0IHRvIGEgdGliYmxlDQogIGFzX3RpYmJsZSgpICU+JSANCg0KICAjIFJlbmFtZSBvdXIgY29sdW1uIGluZm9ybWF0aW9uDQogIHJlbmFtZShDbGFkZSA9IC4uLiwgUHJvdmluY2UgPSAuLi4pICU+JSANCg0KICBtdXRhdGUoQWdlID0gcmVwbGFjZV9uYShBZ2UsIHJlcGxhY2UgPSAwKSkgJT4lIA0KDQogICMgQ29udmVydCB0aGlzIHRvIGEgZGF0YSBmcmFtZQ0KICBhcy5kYXRhLmZyYW1lKCkNCg0KIyBTZXQgdGhlIHJvd25hbWVzIGZyb20gdGhlIGxhYmVsIGluZm9ybWF0aW9uDQpyb3duYW1lcyhjdXJhdGVkX3RyZWVfaW5mby5kZikgPC0gY3VyYXRlZF90cmVlX2luZm8uZGYkbGFiZWwNCg0KIyBMZXQncyB2aWV3IHRoZSB0cmVlDQpzdHIoY3VyYXRlZF90cmVlX2luZm8uZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuOS4wIENvbnZlcnQgeW91ciB0cmVlIHN0eWxlIHdpdGggdGhlIGBsYXlvdXRgIHBhcmFtZXRlcg0KDQpTbyB3ZSd2ZSBvbmx5IGJlZW4gd29ya2luZyB3aXRoIGEgcmVjdGFuZ3VsYXIgdHJlZSAoaG9yaXpvbnRhbCBsYXlvdXQpIHRodXMgZmFyIGJ1dCBhcyB3ZSBtZW50aW9uZWQgYXQgdGhlIGJlZ2lubmluZyB0aGVyZSBhcmUgYWN0dWFsbHkgYSBudW1iZXIgb2YgZGlmZmVyZW50IGxheW91dHMgd2UgY2FuIHVzZS4gT3VyIGdvYWwgbm93IGlzIHRvIG1ha2UgYSBzb3J0IG9mIHN1bmJ1cnN0IHBsb3QgdXNpbmcgYSBjaXJjdWxhciBsYXlvdXQgdGhhdCB3ZSdsbCBhZGQgdmlzdWFsIGNhdGVnb3JpY2FsIGluZm9ybWF0aW9uIHRvIGluIGEgcmluZyBwYXR0ZXJuLg0KDQpGaXJzdCwgd2UnbGwgc3RhcnQgd2l0aCB0aGUgY2lyY3VsYXIgdmVyc2lvbi4gV2UnbGwgY29sb3VyIG91ciB0aXBzIGJ5IHRoZSBwcm92aW5jZSB3aGVyZSB0aGUgY2FzZSBvciBkYXRhIHdhcyBnZW5lcmF0ZWQgYW5kIGxhYmVsIHRoZW0gYnkgdGhlIE5leHRzdHJhaW4gY2xhZGUgaW5mb3JtYXRpb24uDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQojIFNob3cgb3VyIGN1cmF0ZWQgdHJlZSBpbiBhIGNpcmN1bGFyIGZvcm1hdA0KDQp0cmVlLnBsb3QgPC0NCg0KICBTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JQ0KICBkcm9wLnRpcCguLCBjdXJhdGVkX2Ryb3BfbGlzdCkgICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3RyZWUoLiwgbGF5b3V0ID0gLi4uLCAjIyMgMS45LjAgU2V0IHRoZSBsYXlvdXQgdG8gY2lyY3VsYXINCiAgICAgICAgIG1yc2QgPSAiMjAyMS0wMi0xNyIpICsgIyBOZWVkIHRoZSBtb3N0IHJlY2VudCBzYW1wbGluZyBkYXRlDQogICAgIyAyLiBBZXN0aGV0aWNzDQoNCiAgICAjIFRoZW1lcw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgIGxhYnModGl0bGUgPSAiQ2FuYWRhIGFuZCBVUyBzZXF1ZW5jZWQgc3RyYWluIHBoeWxvZ2VueSIsDQogICAgICAgICBjb2xvdXIgPSAiUHJvdmluY2UiKSArDQoNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKG9wdGlvbiA9ICJwbGFzbWEiKSArICMgVGhpcyBzZWVtcyB0byBsaXRlcmFsbHkga2lsbCB0aGUga2VybmVsIQ0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91ciA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSA1KSArIA0KDQogICAgIyMjIDEuOS4wIHNldCB0aGUgdGlwIGxhYmVscw0KICAgIGdlb21fdGlwbGFiKGFlcyhsYWJlbCA9IC4uLiksIHNpemUgPSA4KQ0KDQojIFZpZXcgb3VyIHRyZWUNCnRyZWUucGxvdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjEwLjAgVXNlIGBnaGVhdG1hcCgpYCBsYXllciB0byBhZGQgaW5mb3JtYXRpb24gdG8geW91ciBwbG90DQoNCk5vdyB0aGF0IHdlIGhhdmUgb3VyIGJhc2ljIGNpcmN1bGFyIHRyZWUsIHdlIGNhbiB1c2UgdGhlIGluZm9ybWF0aW9uIGZyb20gYGN1cmF0ZWRfdHJlZV9pbmZvLmRmYCB0byB2aXN1YWxpemUgYWRkaXRpb25hbCBkYXRhIG9uIG91ciB0cmVlLiBJbiBvcmRlciB0byB1c2UgdGhlIGBnaGVhdG1hcGAgbGF5ZXIgcHJvcGVybHksIHdlIG11c3QgcGFzcyBpdCBvdXIgb3JpZ2luYWwgdHJlZSBwbG90LCBhbG9uZyB3aXRoIGEgbmV3IGRhdGFmcmFtZSAob3IgdmVjdG9yKSB0aGF0IGhvbGRzIHRoZSBpbmZvcm1hdGlvbiB3ZSB3YW50IHRvIGFkZC4gSXQgc2hvdWxkIHVzZSB0aGUgcm93bmFtZXMgZnJvbSB0aGUgZGF0YSBmcmFtZSB0byBoZWxwIG1hdGNoIHVwIHRvIHRoZSB0aXBzIGluIG91ciB0cmVlLg0KDQpXZSdsbCBkcm9wIHRoZSB0aXAgbGFiZWxzIGZyb20gb3VyIHRyZWUgaW4gZmF2b3VyIG9mIHRoZSBoZWF0bWFwIHdlJ2xsIGFkZC4gQnkgYWRkaW5nIGxheWVycyBvZiBoaWVyYXJjaGljYWwgZGF0YSwgdGhpcyBjcmVhdGVzIHdoYXQgaXMga25vd24gYXMgYSAqKlN1bmJ1cnN0KiogcGxvdC4NCg0KVGhlIGdoZWF0bWFwKCkgbGF5ZXIgdGFrZXMgc29tZSBvZiB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnM6DQoNCi0gICBgcGA6IHRoZSB0cmVlIHBsb3Qgd2Ugd2FudCB0byBtb2RpZnkuDQoNCi0gICBgZGF0YWA6IHRoZSBkYXRhIHVzZWQgdG8gbW9kaWZ5IHRoZSB0cmVlIHBsb3QgYHBgLg0KDQotICAgYG9mZnNldGA6IGRldGVybWluZXMgdGhlIG9mZnNldCBvZiB0aGUgaGVhdG1hcCByZWxhdGl2ZSB0byB0aGUgdHJlZS4NCg0KLSAgIGB3aWR0aGA6IGRldGVybWluZXMgdGhlIHRvdGFsIHdpZHRoIG9mIHRoZSBoZWF0bWFwcyBjb21wYXJlZCB0byB0aGUgdHJlZQ0KDQotICAgYGNvbG5hbWVzX2FuZ2xlYDogZGV0ZXJtaW5lcyB0aGUgYW5nbGUgb2YgdGhlIHRleHQgZm9yIHRoZSBoZWF0bWFwLg0KDQotICAgYGZvbnQuc2l6ZWA6IHNldHMgdGhlIGZvbnQgc2l6ZSBmb3IgdGhlIGhlYXRtYXAgcG9ydGlvbiBvZiB0aGUgdHJlZS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCiMgU2hvdyBvdXIgY3VyYXRlZCB0cmVlIGluIGEgY2lyY3VsYXIgZm9ybWF0DQoNCnRyZWUucGxvdCA8LQ0KDQogIFNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogIGRyb3AudGlwKC4sIGN1cmF0ZWRfZHJvcF9saXN0KSAgJT4lIA0KDQogICMgMS4gRGF0YQ0KICBnZ3RyZWUoLiwgbGF5b3V0ID0gImNpcmN1bGFyIiwgbXJzZCA9ICIyMDIxLTAyLTE3IikgKyAjIE5lZWQgdGhlIG1vc3QgcmVjZW50IHNhbXBsaW5nIGRhdGUNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICANCiAgICAjIFRoZW1lcw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgIGxhYnModGl0bGUgPSAiQ2FuYWRhIGFuZCBVUyBzZXF1ZW5jZWQgc3RyYWluIHBoeWxvZ2VueSIsDQogICAgICAgICBjb2xvdXIgPSAiUHJvdmluY2UiKSArDQoNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKG9wdGlvbiA9ICJwbGFzbWEiKSArDQoNCiAgICAjIDQuIEdlb21zDQogICAgZ2VvbV90aXBwb2ludChhZXMoY29sb3VyID0gc3RyYWluX2RpdmlzaW9uKSwgc2l6ZSA9IDUpICMgQWRkIHRpcHMgb25seSANCg0KIyMjIDEuMTAuMCBBZGQgYSBjaXJjdWxhciBoZWF0bWFwIHdpdGggZ2hlYXRtYXAoKQ0KdHJlZS5wbG90IDwtIC4uLih0cmVlLnBsb3QsIC4uLiwgDQogICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0ID0gMC4xLCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSA2LCBoanVzdCA9IDEpICsgDQogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lID0gIlRyZWUgY29kaW5nIikgDQoNCg0KdHJlZS5wbG90DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjEwLjEgYGdnbmV3c2NhbGVgIHBhY2thZ2UgYWxsb3dzIG11bHRpcGxlIGNvbG9yL2ZpbGwgc2NhbGVzIG9uIHlvdXIgU3VuYnVyc3QgcGxvdA0KDQpGcm9tIG91ciBzdW5idXJzdCBwbG90IGFib3ZlIHdlIHVzZWQgKm11bHRpcGxlKiBjb2x1bW5zIGZyb20gYGN1cmF0ZWRfdHJlZV9pbmZvLmRmYCBidXQgdGhlIGNhdmVhdCBpcyB0aGF0IHRoZSBsZWdlbmQgZm9yIGVhY2ggcmVzdWx0aW5nIGNvbHVtbiBjb21iaW5lZCBhbGwgb2YgdGhlIGRhdGEgaW50byBhIHNpbmdsZSBuZXcgbGVnZW5kLiBUaGF0J3MgaGVscGZ1bCBpZiB0aGUgKnR5cGUqIG9mIGRhdGEgY291bGQgYmUgY29udGludW91cyB2YWx1ZXMgbGlrZSBhIFswLDFdIHNjYWxlIGhlYXRtYXAgY29sb3VyIGFjcm9zcyBkaWZmZXJlbnQgZmVhdHVyZXMuIFdoZW4gZGVhbGluZyB3aXRoIG11bHRpcGxlIGNhdGVnb3JpZXMgd2hlcmUgdGhlIHNjYWxlIG9mIHRoZSB2YWx1ZXMgdmFyaWVzLCBob3dldmVyLCB0aGF0IGRvZXNuJ3Qgd29yayB3ZWxsIGZvciB1cy4gVGhpcyBhbHNvIGRvZXNuJ3Qgd29yayB3ZWxsIGZvciBjYXRlZ29yaWNhbCBkYXRhLg0KDQpJbnN0ZWFkLCB3ZSdkIGxpa2UgdG8gc2VwYXJhdGUgdGhlIGNvbG91ci9maWxsIHNjYWxlcyBieSByZXBlYXRlZGx5IGFkZGluZyB0byBvdXIgcGxvdCwgbGF5ZXIgYnkgbGF5ZXIuIEFzIHlvdSBtaWdodCByZWNhbGwsIGhvd2V2ZXIsIHdlIGFsc28gcnVuIGludG8gdGhlIHByb2JsZW0gb2YgYHNjYWxlX2NvbG91cl8qYCBhbmQgYHNjYWxlX2ZpbGxfKmAgbGF5ZXJzIG92ZXJ3cml0aW5nIGFueSAqcHJldmlvdXMqIGxheWVycy4NCg0KVG8gY2lyY3VtdmVudCB0aGlzIHJlYWxpdHksIHdlJ2xsIHVzZSB0aGUgYGdnbmV3c2NhbGVgIHBhY2thZ2UgdG8gZ2VuZXJhdGUgbmV3IGNvbG91ciBhbmQgZmlsbCBzY2FsZXMgd2l0aCBgbmV3X3NjYWxlX2ZpbGwoKWAgYW5kIGBuZXdfc2NhbGVfY29sb3VyKClgLiBJdCBjYW4gbWFrZSB0aGUgcHJvY2VzcyBzbGlnaHRseSBtb3JlIGVuY3VtYmVyaW5nIGJ1dCBpdCB3aWxsIHdvcmsgb3V0Lg0KDQpXZSdsbCBhZGQgYmFjayBpbiBvdXIgb3JpZ2luYWwgdGlwIGxhYmVscyBhcyB3ZWxsIGZvciB0aGlzIGdyYXBoLg0KDQpgYGB7ciwgZmlnLndpZHRoPTQ1LCBmaWcuaGVpZ2h0PTQ1fQ0KDQojIFNob3cgb3VyIGN1cmF0ZWQgdHJlZSBpbiBhIGNpcmN1bGFyIGZvcm1hdA0KDQojIFdlJ2xsIG5lZWQgc29tZSBleHRyYSBjb2xvdXIgc2V0cyB0byBhY2NvbXBsaXNoIHRoaXMgcGxvdA0KY29tYm8uY29sb3VycyA9IGMoYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpLCBicmV3ZXIucGFsKDgsICJTZXQxIiksIGJyZXdlci5wYWwoMTIsICJTZXQzIikpDQoNCg0KIyBTYXZlIG91ciBmaXJzdCBwbG90IHdpdGgganVzdCB0aGUgdHJlZQ0KdHJlZS5wbG90IDwtDQoNCiAgU0MyX3ZhcmlhbnRzX3RpbWUudHJlZSAlPiUNCiAgZHJvcC50aXAoLiwgY3VyYXRlZF9kcm9wX2xpc3QpICAlPiUgDQogIA0KICAjIDEuIERhdGENCiAgZ2d0cmVlKC4sIGxheW91dCA9ICJjaXJjdWxhciIsIG1yc2QgPSAiMjAyMS0wMi0xNyIpICsgIyBOZWVkIHRoZSBtb3N0IHJlY2VudCBzYW1wbGluZyBkYXRlDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCksDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICBsYWJzKHRpdGxlID0gIkNhbmFkYSBhbmQgVVMgc2VxdWVuY2VkIHN0cmFpbiBwaHlsb2dlbnkiKSArDQoNCiAgICAjIFdlJ2xsIHdhbnQgdG8gZW5zdXJlIG91ciBjb2xvdXIgZ3VpZGUgZW5kcyB1cCBpbiB0aGUgY29ycmVjdCBvcmRlcg0KICAgIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHJvdmluY2UiLCBvcmRlcj0xKSkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91ciA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSA1KSArICMgQWRkIHRpcHMgb25seSANCiAgICBnZW9tX3RpcGxhYihzaXplID0gMTAsIGFsaWduPVRSVUUsIG9mZnNldCA9IDAuNikgIyBBZGQgaW4gb3VyIGxhYmVscw0KDQoNCiMgR2VuZXJhdGUgb3VyIGZpcnN0IGhlYXRtYXAgbGF5ZXINCnRyZWUucGxvdCA8LSBnaGVhdG1hcCh0cmVlLnBsb3QsIGN1cmF0ZWRfdHJlZV9pbmZvLmRmICU+JSBzZWxlY3QocGFyZW50LCBub2RlLCBicmFuY2gubGVuZ3RoKSwgDQogICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0ID0gMC4xLCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSA2KSArIA0KICAjIFNldCB0aGUgbmFtZSBvZiBvdXIgbGVnZW5kDQogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lID0gIlRyZWUgY29kaW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpIA0KDQojIyMgMS4xMC4xIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgbmV3IGNvbG91ciBzY2FsZQ0KdHJlZS5wbG90IDwtIHRyZWUucGxvdCArIC4uLiANCg0KIyBHZW5lcmF0ZSBhIGNhdGVnb3JpY2FsIGhlYXRtYXAgbGF5ZXIgZm9yIHRoZSBDbGFkZSB2YXJpYWJsZQ0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdChDbGFkZSksIA0KICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IDAuMiwgd2lkdGggPSAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgY29sbmFtZXNfYW5nbGUgPSA5MCwgZm9udC5zaXplID0gMTIpICsgDQogICAgDQogICMgU2V0IHRoZSBuYW1lIGFuZCBvcmRlciBvZiBvdXIgQ2xhZGUgbGVnZW5kDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJOZXh0c3RyYWluXG5DbGFkZSIsIA0KICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyPTMpKQ0KDQojIyMgMS4xMC4xIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgbmV3IGNvbG91ciBzY2FsZQ0KdHJlZS5wbG90IDwtIHRyZWUucGxvdCArIC4uLiANCg0KIyBHZW5lcmF0ZSBhIGNhdGVnb3JpY2FsIGhlYXRtYXAgbGF5ZXIgZm9yIHRoZSBQQU5HTyB2YXJpYWJsZQ0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdCguLi4pLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAwLjM1LCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSAxMikgKyAgICAgDQoNCiAgIyBTZXQgdGhlIG5hbWUgYW5kIG9yZGVyIG9mIG91ciBQQU5HTyBsZWdlbmQNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29tYm8uY29sb3VycywgbmFtZSA9ICJQQU5HT1xubGluZWFnZSIsIA0KICAgICAgICAgICAgICAgICAgICBndWlkZT1ndWlkZV9sZWdlbmQob3JkZXI9NCkpDQoNCiMgWW91IG1heSBuZWVkIHRvIHJlLXJlbmRlciB0byBzZWUgaXQgcHJvcGVybHkgKEV4cGFuZC9jb2xsYXBzZSBhZ2FpbikNCnRyZWUucGxvdA0KYGBgDQoNCiMjIyAxLjEwLjIgQ3JlYXRlIGEgdmVydGljYWwgdmVyc2lvbiBvZiBvdXIgcGxvdCB1c2luZyBhIHJlY3Rhbmd1bGFyIGxheW91dA0KDQpEZXBlbmRpbmcgb24gdGhlIGxldmVsIG9mIGRhdGEgZGVwdGgsIHlvdSBtYXkgYWxzbyBjaG9vc2UgdG8gbGF5ZXIgeW91ciBzY2FsZXMgYWxvbmcgdGhlIHJpZ2h0LWhhbmQgc2lkZSBvZiB5b3VyIHRyZWUuIFRvIGFjY29tcGxpc2ggdGhpcyB3ZSBjYW4gc2ltcGx5IHNldCBvdXIgYGxheW91dGAgcGFyYW1ldGVyIHRvICJyZWN0YW5ndWxhciIuIEF0IHRoZSBzYW1lIHRpbWUsIHdlJ2xsIHJ1biBpbnRvIGEgY291cGxlIG9mIHByb2JsZW1zIHdpdGggaG93IHRoZSBgZ2d0cmVlYCBpcyBtYWRlLg0KDQpPbmUgaXNzdWUgd2l0aCBnZ3Bsb3QgaXMgdGhhdCBhcyB3ZSBhZGQgdGhlIHNjYWxlcywgdGhlc2UgYXJlIHZlcnkgbXVjaCBsaWtlIGFubm90YXRpb25zIG9uIG91ciBnZ3Bsb3QuIFRoZSB2ZXJ0aWNhbCAoYW5kIGhvcml6b250YWwpIGRpc3BsYXkgYXJlYXMgb2Ygb3VyIHBsb3QgYXJlIG5vdCB1cGRhdGVkIGFzIHdlIGFkZCB0aGVzZSBhbm5vdGF0aW9ucy4gV2hlbiB3ZSBhZGQgbmV3IGNvbG91ciBzY2FsZSBsYXllcnMgdG8gdGhlIHBsb3QsIHRoZSBsYWJlbHMgd2lsbCwgaW4gbWFueSBjYXNlcywgYmxlZWQgb3V0c2lkZSBvZiB0aGUgZGVzaWduYXRlZCBwbG90IGFyZWEuIFRvIGNvbWJhdCB0aGlzLCB3ZSBjYW4gYWRkIGEgbGF5ZXIgY2FsbGVkIGB2ZXhwYW5kKClgIG9yIGBoZXhwYW5kKClgIGZvciBob3Jpem9udGFsIGV4cGFuc2lvbi4gQm90aCBvZiB0aGVzZSB0YWtlIDIgcGFyYW1ldGVyczoNCg0KLSAgIGByYXRpb2A6IHRoZSByYXRpbyBvZiBleHBhbnNpb24gZm9yIHlvdXIgcGxvdCBheGlzDQotICAgYGRpcmVjdGlvbmA6IHRoZSBkaXJlY3Rpb24geW91IHdhbnQgdGhlIGV4cGFuc2lvbiBpbiBhIHJhbmdlIGZyb20gMSAobGVmdC90b3ApIHRvIC0xIChyaWdodC9ib3R0b20pLg0KDQpgYGB7ciwgZmlnLndpZHRoPTMwLCBmaWcuaGVpZ2h0PTM1fQ0KDQojIFNob3cgb3VyIGN1cmF0ZWQgdHJlZSBpbiBhIHJlY3Rhbmd1bGFyIGZvcm1hdA0KDQojIFdlJ2xsIG5lZWQgc29tZSBleHRyYSBjb2xvdXIgc2V0cyB0byBhY2NvbXBsaXNoIHRoaXMgcGxvdA0KY29tYm8uY29sb3VycyA9IGMoYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpLCBicmV3ZXIucGFsKDgsICJTZXQxIiksIGJyZXdlci5wYWwoMTIsICJTZXQzIikpDQoNCiMgU2F2ZSBvdXIgZmlyc3QgcGxvdCB3aXRoIGp1c3QgdGhlIHRyZWUNCnRyZWUucGxvdCA8LQ0KDQogIFNDMl92YXJpYW50c190aW1lLnRyZWUgJT4lDQogIGRyb3AudGlwKC4sIGN1cmF0ZWRfZHJvcF9saXN0KSAgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdndHJlZSguLCBsYXlvdXQgPSAuLi4sICMjIyAxLjEwLjIgY29udmVydCB5b3VyIHBsb3QgdG8gYSByZWN0YW5ndWxhciBmb3JtYXQgDQogICAgICAgICBtcnNkID0gIjIwMjEtMDItMTciKSArICMgTmVlZCB0aGUgbW9zdCByZWNlbnQgc2FtcGxpbmcgZGF0ZQ0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIA0KICAgICMgVGhlbWVzDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzApLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICAgKSArDQogICAgbGFicyh0aXRsZSA9ICJDYW5hZGEgYW5kIFVTIHNlcXVlbmNlZCBzdHJhaW4gcGh5bG9nZW55IiwNCiAgICAgICAgIGNvbG91ciA9ICJQcm92aW5jZSIpICsNCg0KICAgICMjIyAxLjEwLjIgRXhwYW5kIHRoZSB2ZXJ0aWNhbCBwbG90IHNpemUNCiAgICB2ZXhwYW5kKHJhdGlvID0gLi4uLCBkaXJlY3Rpb24gPSAuLi4pICsgDQoNCiAgICAjIFdlJ2xsIHdhbnQgdG8gZW5zdXJlIG91ciBjb2xvdXIgZ3VpZGUgZW5kcyB1cCBpbiB0aGUgY29ycmVjdCBvcmRlcg0KICAgIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHJvdmluY2UiLCBvcmRlcj0xKSkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91ciA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSA1KSArICMgQWRkIHRpcHMgb25seSANCiAgICBnZW9tX3RpcGxhYihzaXplID0gMTAsIGFsaWduPVRSVUUpICMgQWRkIGluIG91ciBsYWJlbHMgDQoNCg0KIyBHZW5lcmF0ZSBvdXIgZmlyc3QgaGVhdG1hcCBsYXllcg0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdChwYXJlbnQsIG5vZGUsIGJyYW5jaC5sZW5ndGgpLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAwLjc1LCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSA4LCBoanVzdCA9IDEpICsgDQogICMgU2V0IHRoZSBuYW1lIG9mIG91ciBsZWdlbmQNCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKG5hbWUgPSAiVHJlZSBjb2RpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgDQoNCiMgdXNlIHRoaXMgY29kZSB0byBjcmVhdGUgYSBuZXcgY29sb3VyIHNjYWxlDQp0cmVlLnBsb3QgPC0gdHJlZS5wbG90ICsgbmV3X3NjYWxlX2ZpbGwoKSANCg0KIyBHZW5lcmF0ZSBhIGNhdGVnb3JpY2FsIGhlYXRtYXAgbGF5ZXIgZm9yIHRoZSBDbGFkZSB2YXJpYWJsZQ0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdChDbGFkZSksIA0KICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IDAuODUsIHdpZHRoID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzX2FuZ2xlID0gOTAsIGZvbnQuc2l6ZSA9IDEyLCBoanVzdCA9IDEpICsgDQogICAgDQogICMgU2V0IHRoZSBuYW1lIGFuZCBvcmRlciBvZiBvdXIgQ2xhZGUgbGVnZW5kDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJOZXh0c3RyYWluXG5DbGFkZSIsIA0KICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyPTMpKQ0KDQojIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgbmV3IGNvbG91ciBzY2FsZQ0KdHJlZS5wbG90IDwtIHRyZWUucGxvdCArIG5ld19zY2FsZV9maWxsKCkgDQoNCiMgR2VuZXJhdGUgYSBjYXRlZ29yaWNhbCBoZWF0bWFwIGxheWVyIGZvciB0aGUgUEFOR08gdmFyaWFibGUNCnRyZWUucGxvdCA8LSBnaGVhdG1hcCh0cmVlLnBsb3QsIGN1cmF0ZWRfdHJlZV9pbmZvLmRmICU+JSBzZWxlY3QoUEFOR08pLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAxLjAsIHdpZHRoID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzX2FuZ2xlID0gOTAsIGZvbnQuc2l6ZSA9IDEyLCBoanVzdCA9IDEpICsgICAgIA0KDQogICMgU2V0IHRoZSBuYW1lIGFuZCBvcmRlciBvZiBvdXIgUEFOR08gbGVnZW5kDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbWJvLmNvbG91cnMsIG5hbWUgPSAiUEFOR09cbmxpbmVhZ2UiLCANCiAgICAgICAgICAgICAgICAgICAgZ3VpZGU9Z3VpZGVfbGVnZW5kKG9yZGVyPTQpKQ0KDQojIFZpZXcgdGhlIHRyZWUNCnRyZWUucGxvdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4xMS4wIEFubm90YXRlIGNvbm5lY3Rpb25zIGJldHdlZW4gdGlwcyB3aXRoIGBnZW9tX3RheGFsaW5rKClgDQoNClN1cHBvc2Ugd2Ugd2FudCB0byBqb2luIHRvIHRpcHMvdGF4YSB0b2dldGhlciB0byBzdWdnZXN0IHRoYXQgdGhlcmUgaXMgYSBub24tdHJlZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVtIG9yIHRvIGVtcGhhc2l6ZSBzb21lIGFzc29jaWF0aW9uIGJldHdlZW4gbm9kZXMuIFRvIGNvbm5lY3Qgbm9kZXMgb3IgdGlwcyBvZiB0aGUgdHJlZSwgd2UgY2FuIHVzZSB0aGUgYGdlb21fdGF4YWxpbmsoKWAgbGF5ZXIuDQoNCkxldCdzIGpvaW4gdGhlIG5vZGVzIGBDYW5hZGEvTkwtTk1MLTE3MTk0LzIwMjFgIGFuZCBgQ2FuYWRhL09OLVVIVEMtMDM2Ni8yMDIwYCB0b2dldGhlci4NCg0KYGBge3IsIGZpZy53aWR0aD0zMCwgZmlnLmhlaWdodD0zMH0NCg0KIyBTaG93IG91ciBjdXJhdGVkIHRyZWUgaW4gYSByZWN0YW5ndWxhciBmb3JtYXQNCg0KIyBXZSdsbCBuZWVkIHNvbWUgZXh0cmEgY29sb3VyIHNldHMgdG8gYWNjb21wbGlzaCB0aGlzIHBsb3QNCmNvbWJvLmNvbG91cnMgPSBjKGJyZXdlci5wYWwoMTIsICJQYWlyZWQiKSwgYnJld2VyLnBhbCg4LCAiU2V0MSIpLCBicmV3ZXIucGFsKDEyLCAiU2V0MyIpKQ0KDQojIFNhdmUgb3VyIGZpcnN0IHBsb3Qgd2l0aCBqdXN0IHRoZSB0cmVlDQp0cmVlLnBsb3QgPC0NCg0KICBTQzJfdmFyaWFudHNfdGltZS50cmVlICU+JQ0KICBkcm9wLnRpcCguLCBjdXJhdGVkX2Ryb3BfbGlzdCkgICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2d0cmVlKC4sIGxheW91dCA9ICJyZWN0YW5ndWxhciIsIG1yc2QgPSAiMjAyMS0wMi0xNyIpICsgIyBOZWVkIHRoZSBtb3N0IHJlY2VudCBzYW1wbGluZyBkYXRlDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCksDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgICAgKSArDQogICAgbGFicyh0aXRsZSA9ICJDYW5hZGEgYW5kIFVTIHNlcXVlbmNlZCBzdHJhaW4gcGh5bG9nZW55IiwNCiAgICAgICAgIGNvbG91ciA9ICJQcm92aW5jZSIpICsNCiAgICB2ZXhwYW5kKHJhdGlvID0gMC4xLCBkaXJlY3Rpb24gPSAtMSkgKyAjIEV4cGFuZCB0aGUgdmVydGljYWwgcGxvdCBzaXplDQoNCiAgICAjIFdlJ2xsIHdhbnQgdG8gZW5zdXJlIG91ciBjb2xvdXIgZ3VpZGUgZW5kcyB1cCBpbiB0aGUgY29ycmVjdCBvcmRlcg0KICAgIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHJvdmluY2UiLCBvcmRlcj0xKSkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91ciA9IHN0cmFpbl9kaXZpc2lvbiksIHNpemUgPSA1KSArICMgQWRkIHRpcHMgb25seSANCiAgICBnZW9tX3RpcGxhYihzaXplID0gMTAsIGFsaWduPVRSVUUpICsgIyBBZGQgaW4gb3VyIGxhYmVscw0KDQogICAgIyMjIDEuMTEuMCBBZGQgaW4gc29tZSBhbm5vdGF0aW9uIGJldHdlZW4gcG9pbnRzDQogICAgZ2VvbV90YXhhbGluayh0YXhhMT0uLi4sIHRheGEyPS4uLiwgDQogICAgICAgICAgICAgICAgICBjb2xvcj0iYmx1ZSIsIGFscGhhPTAuOCwgb2Zmc2V0PTAsIA0KICAgICAgICAgICAgICAgICAgb3V0d2FyZD1GQUxTRSwNCiAgICAgICAgICAgICAgICAgIGFycm93PWFycm93KGxlbmd0aD11bml0KDAuMDEsICJucGMiKSkpDQoNCg0KIyBHZW5lcmF0ZSBvdXIgZmlyc3QgaGVhdG1hcCBsYXllcg0KdHJlZS5wbG90IDwtIGdoZWF0bWFwKHRyZWUucGxvdCwgY3VyYXRlZF90cmVlX2luZm8uZGYgJT4lIHNlbGVjdChwYXJlbnQsIG5vZGUsIGJyYW5jaC5sZW5ndGgpLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAwLjc1LCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDkwLCBmb250LnNpemUgPSA4LCBoanVzdCA9IDEpICsgDQogIA0KICAjIFNldCB0aGUgbmFtZSBvZiBvdXIgbGVnZW5kDQogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lID0gIlRyZWUgY29kaW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpIA0KDQojIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgbmV3IGNvbG91ciBzY2FsZQ0KdHJlZS5wbG90IDwtIHRyZWUucGxvdCArIG5ld19zY2FsZV9maWxsKCkgDQoNCiMgR2VuZXJhdGUgYSBjYXRlZ29yaWNhbCBoZWF0bWFwIGxheWVyIGZvciB0aGUgQ2xhZGUgdmFyaWFibGUNCnRyZWUucGxvdCA8LSBnaGVhdG1hcCh0cmVlLnBsb3QsIGN1cmF0ZWRfdHJlZV9pbmZvLmRmICU+JSBzZWxlY3QoQ2xhZGUpLCANCiAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSAwLjg1LCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDAsIGZvbnQuc2l6ZSA9IDEwKSArIA0KICAgIA0KICAjIFNldCB0aGUgbmFtZSBhbmQgb3JkZXIgb2Ygb3VyIENsYWRlIGxlZ2VuZA0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiTmV4dHN0cmFpblxuQ2xhZGUiLCANCiAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlcj0zKSkNCg0KIyB1c2UgdGhpcyBjb2RlIHRvIGNyZWF0ZSBhIG5ldyBjb2xvdXIgc2NhbGUNCnRyZWUucGxvdCA8LSB0cmVlLnBsb3QgKyBuZXdfc2NhbGVfZmlsbCgpIA0KDQojIEdlbmVyYXRlIGEgY2F0ZWdvcmljYWwgaGVhdG1hcCBsYXllciBmb3IgdGhlIFBBTkdPIHZhcmlhYmxlDQp0cmVlLnBsb3QgPC0gZ2hlYXRtYXAodHJlZS5wbG90LCBjdXJhdGVkX3RyZWVfaW5mby5kZiAlPiUgc2VsZWN0KFBBTkdPKSwgDQogICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0ID0gMS4wLCB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lc19hbmdsZSA9IDAsIGZvbnQuc2l6ZSA9IDEwKSArICAgICANCg0KICAjIFNldCB0aGUgbmFtZSBhbmQgb3JkZXIgb2Ygb3VyIFBBTkdPIGxlZ2VuZA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb21iby5jb2xvdXJzLCBuYW1lID0gIlBBTkdPXG5saW5lYWdlIiwgDQogICAgICAgICAgICAgICAgICAgIGd1aWRlPWd1aWRlX2xlZ2VuZChvcmRlcj00KSkNCg0KIyBWaWV3IHRoZSB0cmVlDQp0cmVlLnBsb3QNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS4xMi4wIFRoZXJlIGlzIGRlZmluaXRlbHkgbW9yZSB0byBleHBsb3JlIGZyb20gdGhlIGBnZ3RyZWVgIHBhY2thZ2UNCg0KU28gd2UndmUgc3BlbnQgcXVpdGUgYSBiaXQgb2YgdGltZSBsb29raW5nIGF0IHBoeWxvZ2VuZXRpYyB0cmVlcyBhbmQgaG93IHRvIGFkZCBleHRlcm5hbCBkYXRhIHRvIHRpcHMgYW5kIG5vZGVzLiBXZSd2ZSBiYXJlbHkgc2NyYXRjaGVkIHRoZSBzdXJmYWNlIGFuZCB0aGVyZSBhcmUgYSBsb3Qgb2YgYWRkaXRpb25hbCBmdW5jdGlvbnMgd2l0aGluIHRoZSBgZ2d0cmVlYCBwYWNrYWdlIHRoYXQgeW91IGNhbiB3b3JrIHdpdGggdG8gYnVpbGQgYW1hemluZyBwbG90cyBpbmNsdWRpbmcgYWRkaW5nIGZhc3RhIHNlcXVlbmNlIGRhdGEgdGhyb3VnaCBgbXNhcGxvdCgpYC4gWW91IGNhbiBleHBlcmltZW50IHdpdGggbGFiZWxpbmcgaW50ZXJuYWwgbm9kZXMsIGFuZCBzcGVjaWZpYyBjbGFkZXMgYXMgd2VsbC4gWW91IGNhbiBhbHNvIGBmYWNldF9wbG90KClgIHJlY3Rhbmd1bGFyIHRyZWVzIHdpdGggb3RoZXIga2luZHMgb2YgcGxvdHMhDQoNCldlIGRpZG4ndCBldmVuIGhhdmUgdGltZSB0byBjb21iaW5lIGFsbCBzb3J0cyBvZiBwbG90IGRhdGEgd2l0aCBvdXIgY2lyY3VsYXIgdHJlZXMgdXNpbmcgdGhlIGBnZ3RyZWVFeHRyYWAgcGFja2FnZS4gRGVmaW5pdGVseSBjaGVjayBvdXQgW0NoYXB0ZXIgMTBdKGh0dHBzOi8veXVsYWItc211LnRvcC90cmVlZGF0YS1ib29rL2NoYXB0ZXIxMC5odG1sKSBvZiB0aGUgdHJlZSBib29rIHRvIGJlIGluc3BpcmVkIGJ5IHRoZSBwb3RlbnRpYWwgdGhpbmdzIHlvdSBjb3VsZCBkbyB3aXRoIG1vcmUgY29tcGxleCBkYXRhc2V0cyBsaWtlIHRoaXM6DQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovZ2d0cmVlX2dndHJlZUV4dHJhX21pY3JvYmlvbWUucG5nP3Jhdz10cnVlIiB3aWR0aD0iMTAwMCIvPg0KDQpUaGUgZ2VvbV9mcnVpdCgpIGxheWVyIGNhbiBiZSB1c2VkIHRvIGFkZCBleHRyYSB2aXN1YWxpemF0aW9uIHRvIHlvdXIgc3VuYnVyc3QgcGxvdCB0aHJvdWdoIHRoZSBnZ3RyZWVFeHRyYSBwYWNrYWdlDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMi4wLjAgV2hhdCBhcmUgbmV0d29yayBkaWFncmFtcyBhbmQgaG93IGNhbiB3ZSB1c2UgdGhlbT8NCg0KV2UndmUgc2VlbiBhIGxvdCBvZiB0cmVlcyB0b2RheSBidXQgYSBjbG9zZWx5IHJlbGF0ZWQgc3RydWN0dXJlIGlzIHRoZSBuZXR3b3JrIGRpYWdyYW0uIFRoZXkgc2hhcmUgc2ltaWxhciBjb25jZXB0cyBpbiB0aGF0IGJvdGggaGF2ZSBub2RlcyBhbmQgYnJhbmNoZXMuIEhvd2V2ZXIsIG5vZGVzIGFyZSBjYWxsZWQgKip2ZXJ0aWNlcyoqIGFuZCBjYW4gcmVwcmVzZW50IGFsbW9zdCBhbnl0aGluZywgYnJhbmNoZXMgYmV0d2VlbiBub2RlcyBhcmUgKiplZGdlcyoqIGFuZCBjYW4gc3BhbiBhY3Jvc3MgbXVsdGlwbGUgbm9kZXMsIGluc3RlYWQgb2YgaW4gYSBiaWZ1cmNhdGluZyB0cmVlIHJlbGF0aW9uc2hpcC4gUmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZlcnRpY2VzIGNhbiBiZSBiaWRpcmVjdGlvbmFsLCBhbmQgZGVwZW5kaW5nIG9uIHdoYXQgeW91J2QgbGlrZSB0byBwcmVzZW50LCBtdWx0aXBsZSBwYXJhbGxlbCBlZGdlcyBtYXkgZXhpc3QuIFdlIHNhdyBhIHNwZWNpZmljIHZlcnNpb24gb2YgbmV0d29yayBkaWFncmFtcyBpbiBMZWN0dXJlIDA0IHdpdGggb3VyIFNhbmtleSBwbG90cyENCg0KTmV0d29yayBncmFwaHMgYXJlIGdyZWF0IGZvciBzaG93aW5nIHRoZSBpbnRlcmNvbm5lY3Rpb25zIGJldHdlZW4gZW50aXRpZXMgd2l0aGluIHlvdXIgZGF0YS4gVGhpcyBraW5kIG9mIHZpc3VhbGl6YXRpb24gY2FuIGFsc28gaGVscCB1cyByZWFsaXplIHdoZXJlIHN1YnRsZSBjb25uZWN0aW9ucyBvY2N1ciAob3IgZG9uJ3Qgb2NjdXIhKS4gRGVwZW5kaW5nIG9uIGhvdyB5b3UndmUgd2VpZ2h0ZWQgb3IgY2hvc2VuIGVkZ2VzLCB5b3UgY2FuIGNvbnZleSBob3cgc3Ryb25nIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBlbnRpdGllcyBhcmUgYXMgd2VsbC4NCg0KVG8gd29yayB3aXRoIGdyYXBoIGRhdGEgYW5kIHBsb3QgaXQsIHdlJ2xsIGJlIHVzaW5nIGEgY291cGxlIG9mIGNvbXBhbmlvbiBwYWNrYWdlczogYHRpZHlncmFwaGAgYW5kIGBnZ3JhcGhgLiBNb3JlIGFib3V0IHRoYXQgbGF0ZXIhDQoNClNpbmNlIHdlIGFscmVhZHkgaGF2ZSBzb21lIG1ldGFkYXRhIGFib3V0IENPVklEIGdlbm9tZXMsIGxldCdzIHNlZSBpZiB3ZSBjYW4ndCBjb252ZXJ0IHNvbWUgb2YgaXQgdG8gYSBuZXR3b3JrIGRpYWdyYW0gdG8gYmV0dGVyIHVuZGVyc3RhbmQgc3RyYWluIGluZm9ybWF0aW9uPyBXZSdsbCBiZWdpbiBieSBzZWxlY3Rpbmcgc29tZSBpbmZvcm1hdGlvbiBmcm9tIG91ciBOZXh0c3RyYWluIG1ldGFkYXRhIHNldC4NCg0KYGBge3J9DQpzdHIoU0MyX21ldGFkYXRhLmRmLCBnaXZlLmF0dHIgPSBGQUxTRSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMC4xIFBlcmZvcm0gYSBiaXQgb2YgZGF0YSB3cmFuZ2xpbmcgdG8gc2VsZWN0IG91ciBkYXRhDQoNCkJlZm9yZSB3ZSBqdW1wIGludG8gbWFraW5nIHNvbWUgbmV0d29yayBkaWFncmFtcywgd2UnbGwgd2FudCB0byB0cmltIGRvd24gdGhlIGRhdGEgZnJvbSBgU0MyX21ldGFkYXRhLmRmYC4gV2UgZG9uJ3QgbmVlZCBhbGwgb2YgdGhlIGRhdGEgdGhhdCBpcyBjdXJyZW50bHkgaW4gdGhpcyB0YWJsZS4gSW5zdGVhZCB3ZSdsbCB0cmltIGl0IGRvd24gdG8gMTAgdmFyaWFibGVzOg0KDQotICAgYFN0cmFpbmA6IHRoZSBzdHJhaW4gbmFtZSBvZiB0aGUgZ2Vub21lLiBUaGlzIG1hdGNoZXMgdGhlIHRpcHMgb2Ygb3VyIG9yaWdpbmFsIHRyZWVzIHdlIG1hZGUgaW4gc2VjdGlvbiAxLjAuMC4NCi0gICBgQ291bnRyeWA6IHRoZSBjb3VudHJ5IHdoZXJlIHRoZSBjYXNlIHdhcyByZXBvcnRlZC4NCi0gICBgQWRtaW4gRGl2aXNpb25gOiB0aGUgc3ViLXJlZ2lvbiB3aGVyZSB0aGUgY2FzZSB3YXMgaWRlbnRpZmllZA0KLSAgIGBBZG1pbiBEaXZpc2lvbiBvZiBleHBvc3VyZWA6IHRoZSBzdWItcmVnaW9uIGJlbGlldmVkIHRvIGJlIHRoZSBzb3VyY2Ugb2YgdGhlIHN0cmFpbi4NCi0gICBgUmVnaW9uYDogYXBwZWFycyB0byBiZSB0aGUgY29udGluZW50IHdoZXJlIHRoZSBjYXNlIHdhcyBpZGVudGlmaWVkLg0KLSAgIGBSZWdpb24gb2YgZXhwb3N1cmVgOiB0aGUgY29udGluZW50IHdhcyB0aGUgY2FzZSB3YXMgY29udHJhY3RlZC4NCi0gICBgTmV4dHN0cmFpbiBjbGFkZWA6IHRoZSBOZXh0c3RyYWluIGNsYWRlIGZvciB0aGlzIHN0cmFpbg0KLSAgIGBHSVNBSUQgY2xhZGVgOiB0aGUgY2xhZGUgY2xhc3NpZmljdGlvbiBkZWZpbmVkIGJ5IHRoZSBHbG9iYWwgSW5pdGlhdGl2ZSBvbiBTaGFyaW5nIEF2aWFuIEluZmx1ZW56YSBEYXRhDQotICAgYFBBTkdPIGxpbmVhZ2VgOiB0aGUgUGh5bG9nZW5ldGljIEFzc2lnbm1lbnQgb2YgTmFtZWQgR2xvYmFsIE91dGJyZWFrIGxpbmVhZ2UNCi0gICBgQ29sbGVjdGlvbiBEYXRhYDogdGhlIGNvbGxlY3Rpb24gZGF0ZSBvZiB0aGUgc3RyYWluIChhbHNvIG1pc3NwZWxsZWQpDQoNCmBgYHtyfQ0KU0MyX2dyYXBoX2luZm8uZGYgPC0NCg0KIyBQYXNzIGFsb25nIG91ciBtZXRhZGF0YQ0KU0MyX21ldGFkYXRhLmRmICU+JSANCg0KICAjIFdlJ2xsIGdyYWIgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBjYXNlIHRoYXQgcmVsYXRlcyB0byBpdHMgZ2VvZ3JhcGhpY2FsIHJlZ2lvbg0KICAuLi4oU3RyYWluLCBDb3VudHJ5LCAnQWRtaW4gRGl2aXNpb24nLCAnQWRtaW4gRGl2aXNpb24gb2YgZXhwb3N1cmUnLCAnUmVnaW9uIG9mIGV4cG9zdXJlJywgUmVnaW9uLA0KICAgICAgICAgJ05leHRzdHJhaW4gY2xhZGUnLCAnR0lTQUlEIGNsYWRlJywgJ1BBTkdPIGxpbmVhZ2UnLCAnQ29sbGVjdGlvbiBEYXRhJykgJT4lIA0KICANCiAgIyBEbyBhIGJ1bmNoIG9mIHJlbmFtaW5nIGZvciBvdXIgdmFyaWFibGVzDQogIC4uLihzb3VyY2VfbG9jYXRpb24gPSAnQWRtaW4gRGl2aXNpb24gb2YgZXhwb3N1cmUnLCANCiAgICAgICAgIGNhc2VfbG9jYXRpb24gPSAnQWRtaW4gRGl2aXNpb24nLA0KICAgICAgICAgc291cmNlX3JlZ2lvbiA9ICdSZWdpb24gb2YgZXhwb3N1cmUnLA0KICAgICAgICAgY2FzZV9yZWdpb24gPSAnUmVnaW9uJywNCiAgICAgICAgIGNvbGxlY3Rpb25fZGF0ZSA9ICdDb2xsZWN0aW9uIERhdGEnLA0KICAgICAgICAgTmV4dHN0cmFpbiA9ICdOZXh0c3RyYWluIGNsYWRlJywNCiAgICAgICAgIEdJU0FJRCA9ICdHSVNBSUQgY2xhZGUnLA0KICAgICAgICAgUEFOR08gPSAnUEFOR08gbGluZWFnZScNCiAgICAgICAgKQ0KDQpoZWFkKFNDMl9ncmFwaF9pbmZvLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjEuMCBVc2UgdGhlIGB0aWR5Z3JhcGhgIHBhY2thZ2UgdG8gaGVscCBwcmVwYXJlIGRhdGENCg0KTm93IHRoYXQgd2UgaGF2ZSBzb21lIGluZm9ybWF0aW9uIHRoYXQgd2Ugd2FudCB0byB3b3JrIHdpdGgsIHdlIHdhbnQgdG8gY29udmVydCB0aGF0IGtpbmQgb2YgZGF0YSB0byBzb21ldGhpbmcgdGhhdCBjYW4gYmUgaW50ZXJwcmV0ZWQgaW50byBhIGdyYXBoLiBUaGUgYHRpZHlncmFwaGAgcGFja2FnZSBwcm92aWRlcyBhIHdheSB0byBob29rIGdyYXBoIGRhdGEgaW50byB0aGUgYHRpZHl2ZXJzZWAgc28gdGhhdCB3ZSBjYW4gdXNlIGNvbW1vbiB2ZXJicyBhbmQgaWRlYXMgdG8gZmlsdGVyIGFuZCB3b3JrIHdpdGggaXQuIFVzaW5nIHRoaXMgcGFja2FnZSB3ZSBjYW4gY29udmVydCBvdXIgZGF0YWZyYW1lIGluZm9ybWF0aW9uIGludG8gYSBgdGJsX2dyYXBoYCBvYmplY3Qgd2hpY2ggaXMgYWN0dWFsbHkgYW4gYGlncmFwaGAgb2JqZWN0Lg0KDQpUaGUgZnVuY3Rpb24gd2UnbGwgdXNlIHRvIGNvbnZlcnQgb3VyIGRhdGEgaXMgYGFzX3RibF9ncmFwaCgpYCBidXQgaXQgaGFzIHNvbWUgZXhwZWN0YXRpb25zIGFib3V0IHRoZSBkYXRhLiBUaGUgcGFyYW1ldGVycyBvZiB0aGlzIGZ1bmN0aW9uIGFyZToNCg0KLSAgIGB4YDogdGhlIGRhdGEgZnJhbWUgd2UnZCBsaWtlIHRvIGNvbnZlcnQgdG8gYW4gYGlncmFwaGAuDQoNCi0gICBgbm9kZXNgOiBhIGRhdGEgZnJhbWUgd2l0aCBvdXIgbm9kZSBpbmZvcm1hdGlvbi4NCg0KLSAgIGBlZGdlc2A6IGEgZGF0YSBmcmFtZSBvZiB0d28gY29sdW1ucyBjb250YWluaW5nIGludGVnZXJzIG1hdGNoaW5nIG5vZGUgaW5mb3JtYXRpb24gdG8gZGVzY3JpYmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG5vZGVzLg0KDQpJZiB5b3UgaGF2ZSBib3RoIGEgbm9kZXMgYW5kIGVkZ2VzIGRhdGEgZnJhbWUgeW91IGNhbiBjZXJ0YWlubHkgZ2VuZXJhdGUgYSB0YWJsZSB0aGlzIHdheS4gSG93ZXZlciwgd2UgaGF2ZSBhIGNvbXBsZXggZGF0YWZyYW1lIGFuZCB3ZSB3YW50IHRvIHRyYWNrIGFsbCBvZiB0aGUgaW5mb3JtYXRpb24gaW4gaXQuIFRvIHRoYXQgZW5kIHdlIHdpbGwgKmFkZCogY29sdW1ucyBgZnJvbWAgYW5kIGB0b2AgYmFzZWQgb24gdmFyaWFibGVzIHRoYXQgYWxyZWFkeSBleGlzdCBhbmQgdGhlIGdyYXBoIG5vZGVzIHdpbGwgYmUgZ2VuZXJhdGVkIGZyb20gdGhpcyBpbmZvcm1hdGlvbi4gV2hlbiB3ZSBwcm92aWRlIHRoZSBkYXRhIGZyYW1lLCB0aGUgZnVuY3Rpb24gd2lsbCByZWNvZ25pemUgdGhlIGNvbHVtbnMgcHJlc2VudCBhbmQgcHJvZHVjZSBub2RlLWJhc2VkIGRhdGEgYW5kIGdlbmVyYXRlIGVkZ2UgY2hhcmFjdGVyaXN0aWNzIGZyb20gb3VyIG90aGVyIGNvbHVtbnMuDQoNCmBgYHtyfQ0KIyBOb3cgd2UnbGwgYWRkIHNvbWUgc3BlY2lmaWMgdmFyaWFibGVzIHVzZWQgdG8gZ2VuZXJhdGUgb3VyIGdyYXBoIHRhYmxlDQoNClNDMl9ncmFwaF9pbmZvLmRmICU+JSANCg0KICAjIENyZWF0ZSBvdXIgImZyb20iIGFuZCAidG8iIGNvbHVtbnMgaW4gb3VyIGRhdGEgZnJhbWUNCiAgbXV0YXRlKGZyb20gPSBzb3VyY2VfbG9jYXRpb24sDQogICAgICAgICB0byA9IENvdW50cnkNCiAgICAgICAgKSAlPiUgIA0KDQogICMgRmlsdGVyIGZvciBqdXN0IHN0cmFpbiBkYXRhIGZyb20gQ2FuYWRhIGFuZCBBc2lhDQogIGZpbHRlcihDb3VudHJ5ICVpbiUgYyguLi4pKSAlPiUgDQoNCiAgIyBDb252ZXJ0IHRvIGFuIGlncmFwaA0KICBhc190YmxfZ3JhcGgoKSAlPiUgDQoNCiAgIyBMb29rIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGlncmFwaA0KICBzdHIoKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjIuMCBQbG90IHlvdXIgZ3JhcGggaW5mb3JtYXRpb24gd2l0aCBgZ2dyYXBoKClgDQoNClRoZSBgZ2dyYXBoKClgIGZ1bmN0aW9uIGlzIG9uZSBvZiBtYW55IGZyb20gYSBwYWNrYWdlIG9mIHRoZSBzYW1lIG5hbWUuIFRoaXMgcGFja2FnZSBhZGRzIGdlb21zIGFuZCBhcmNoaXRlY3R1cmUgdGhhdCBpcyBjb21wYXRpYmxlIHdpdGggYGdncGxvdDJgIChmb3IgdGhlIG1vc3QgcGFydCkuIFNvbWUgb2YgdGhlIGZ1bmN0aW9ucyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBhcmU6DQoNCnwgQ29tcG9uZW50IHwgRnVuY3Rpb24gICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IEdyYXBoICAgICB8IGdncmFwaCAgICAgICAgICAgICB8IFRoZSBlcXVpdmFsZW50IG9mIHRoZSBjYWxsIHRvIGBnZ3Bsb3RgLCBpdCBzZXRzIHVwIHRoZSBiYXNpYyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZ3JhcGggcGxvdCAgICB8DQp8IEVkZ2UgICAgICB8IGdlb21fZWRnZV9saW5rICAgICB8IFByb2R1Y2VzIGVkZ2VzIGJldHdlZW4gcG9pbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEVkZ2UgICAgICB8IGdlb21fZWRnZV9mYW4gICAgICB8IFByb2R1Y2VzIGVkZ2VzIGJldHdlZW4gcG9pbnRzIGJ1dCBhY2NvdW50cyBmb3IgcGFyYWxsZWwgZWRnZXMsIGNyZWF0aW5nIGFyY3MgdGhhdCBmYW4gb3V0ICAgICAgICB8DQp8IEVkZ2UgICAgICB8IGdlb21fZWRnZV9wYXJhbGxlbCB8IFByb2R1Y2VzIG11bHRpcGxlIGVkZ2VzIGJldHdlZW4gcG9pbnRzIHdpdGggcGFyYWxsZWwgbGluZXMgcmVwcmVzZW50aW5nIHBhcmFsbGVsIGVkZ2VzICAgICAgICAgICB8DQp8IEVkZ2UgICAgICB8IGdlb21fZWRnZV9sb29wICAgICB8IFVzZWQgdG8gcmVwcmVzZW50IGVkZ2VzIHRoYXQgc3RhcnQgYW5kIGVuZCBhdCB0aGUgc2FtZSBub2RlLiBEb2VzIG5vdCBhY2NvdW50IGZvciBwYXJhbGxlbCBlZGdlcyB8DQp8IE5vZGUgICAgICB8IGdlb21fbm9kZV9wb2ludCAgICB8IEFkZCBiYXNpYyBub2RlcyB0byB5b3VyIGdyYXBoICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IE5vZGUgICAgICB8IGdlb21fbm9kZV9jaXJjbGUgICB8IEFkZCBub2RlcyB0byB5b3VyIGdyYXBoIHRoYXQgY2FuIGJlIHNjYWxlZCBieSB0aGUgY29vcmRpbmF0ZSBzeXN0ZW0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQoNClRoZSBgZ2dyYXBoKClgIGZ1bmN0aW9uIHRha2VzIGluIDMgcGFyYW1ldGVyczoNCg0KLSAgIGBncmFwaGA6IHRoZSBgaWdyYXBoYCBvYmplY3QgdGhhdCB3ZSdsbCBiYXNlIHRoZSBncmFwaCBvbi4NCg0KLSAgIGBsYXlvdXRgOiB0aGUgdHlwZSBvZiBsYXlvdXQgZm9yIHRoZSBncmFwaC4gVGhlcmUgYXJlIG1hbnkgb3B0aW9ucyBpbmNsdWRpbmc6IGF1dG8sIGRlbmRyb2dyYW0sIGxpbmVhciwgbWF0cml4LCB0cmVlbWFwICh5ZXAhKSBjaXJjbGVwYWNrLCBwYXJ0aXRpb24sIGFuZCBoaXZlLg0KDQotICAgYGNpcmN1bGFyYDogYSBsb2dpY2FsIHN0YXRpbmcgd2hldGhlciB0aGUgbGF5b3V0IHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBpbnRvIGEgcmFkaWFsIHJlcHJlc2VudGF0aW9uLiBJdCBjYW4ndCBiZSBhcHBsaWVkIHRvIGFsbCBsYXlvdXRzICh0aGluayBgcG9sYXJfY29vcmRgKS4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0NCg0KIyBOb3cgd2UnbGwgYWRkIHNvbWUgc3BlY2lmaWMgdmFyaWFibGVzIHVzZWQgdG8gZ2VuZXJhdGUgb3VyIGdyYXBoIHRhYmxlDQoNClNDMl9ncmFwaF9pbmZvLmRmICU+JSANCg0KICAjIEdlbmVyYXRlIG91ciBmcm9tIGFuZCB0byBlZGdlIGluZm9ybWF0aW9uDQogIG11dGF0ZShmcm9tID0gc291cmNlX3JlZ2lvbiwNCiAgICAgICAgdG8gPSBjYXNlX3JlZ2lvbg0KICAgICAgICApICU+JSAgDQoNCiAgIyBDb252ZXJ0IHRvIGFuIGlncmFwaCBhbmQgcGFzcyBpdCBvbiB0byBiZSBwbG90dGVkDQogIGFzX3RibF9ncmFwaCgpICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2dyYXBoKC4pICsgDQogICAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKw0KDQogICAgICAjIyMgMi4yLjAgQWRkIG91ciBlZGdlcw0KICAgICAgLi4uKGFlcyhjb2xvdXIgPSBOZXh0c3RyYWluKSwgYXJyb3c9YXJyb3coKSwgd2lkdGggPSAxKSArDQoNCiAgICAgICMjIyAyLjIuMCBBZGQgb3VyIG5vZGVzIGFuZCBsYWJlbCB0aGVtDQogICAgICAuLi4oc2l6ZSA9IDcpICsgDQogICAgICAuLi4oYWVzKGxhYmVsID0gbmFtZSksIHNpemUgPSA1LCByZXBlbCA9IFRSVUUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuMSBVc2UgYGdlb21fZWRnZV9mYW4oKWAgdG8gdmlzdWFsaXplIHBhcmFsbGVsIGVkZ2VzDQoNClNvIGZyb20gb3VyIGdyYXBoIHdlIGNhbiBzZWUgc29tZSBjaGFyYWN0ZXJpc3RpY3MgYWJvdXQgaG93IG91ciBjYXNlIGRhdGEgaXMgY29ubmVjdGVkLiBBbHRob3VnaCAqbW9zdCogb2YgdGhlIGRhdGEgaXMgY2VudHJlZCBhcm91bmQgZ2Vub21lcyBzZXF1ZW5jZWQgaW4gTm9ydGggQW1lcmljYSwgd2UgY2FuIHNlZSB0aGF0ICJOb3J0aCBBbWVyaWNhIiBpcyBhbHNvIGEgc291cmNlIHJlZ2lvbi4NCg0KVGhlIHdheSB3ZSBzZWUgdGhlIGVkZ2VzLCBob3dldmVyIGFsc28gaGFzIGFsbCBvZiB0aGVtIG92ZXJsYXlpbmcgb24gdG9wIG9mIGVhY2ggb3RoZXIuIFdlIGtub3cgdGhhdCB0aGlzIGlzbid0IGdvaW5nIHRvIGJlIGEgMToxIHJlbGF0aW9uc2hpcCBhbmQgYWx0aG91Z2ggd2UgaGF2ZSBjb2xvdXJlZCB0aGUgZWRnZXMsIHdlIG9ubHkgc2VlIHRoZSB0b3Btb3N0IGVkZ2UgaW4gYSBzdGFjayBvZiAqbWFueSouIEZvciBhIGdyYXBoIGxpa2UgdGhpcywgaWYgd2Ugd2FudCB0byBzZWUgYWxsIG9mIHRoZSBlZGdlcywgd2UgY2FuIHVzZSB0aGUgYGdlb21fZWRnZV9mYW4oKWAgbGF5ZXIgd2hpY2ggd2lsbCBoZWxwIHVzIHNlZSBhbGwgb2Ygb3VyIHBhcmFsbGVsIGVkZ2VzIGluIGFsbCBvZiB0aGVpciBzcGxlbmRvdXIuIE9mIG5vdGUsIHdlIGNhbiBhbHNvIHVzZSBgZ2VvbV9lZGdlX3BhcmFsbGVsKClgIGFzIGEgbGF5ZXIuIFdoaWxlIHRoaXMgbGF5ZXIgbWF5IG1ha2UgdGhlIG51bWJlciBvZiBjb25uZWN0aW9ucyBjbGVhcmVyLCBpdCBjYW4gZ2V0IHF1aXRlIGNyb3dkZWQuDQoNCkxldCdzIHRyeSBpdCBvdXQuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTJ9DQojIE5vdyB3ZSdsbCBhZGQgc29tZSBzcGVjaWZpYyB2YXJpYWJsZXMgdXNlZCB0byBnZW5lcmF0ZSBvdXIgZ3JhcGggdGFibGUNCg0KU0MyX2dyYXBoX2luZm8uZGYgJT4lIA0KDQogICMgR2VuZXJhdGUgb3VyIGZyb20gYW5kIHRvIGVkZ2UgaW5mb3JtYXRpb24NCiAgbXV0YXRlKGZyb20gPSBzb3VyY2VfcmVnaW9uLA0KICAgICAgICB0byA9IGNhc2VfcmVnaW9uDQogICAgICAgICkgJT4lICANCg0KICAjIENvbnZlcnQgdG8gYW4gaWdyYXBoDQogIGFzX3RibF9ncmFwaCgpICU+JSANCg0KICAjIDEuIERhdGENCg0KICBnZ3JhcGgoLikgKyANCiAgICAgICMgMi4gQWVzdGhldGljcw0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArDQoNCiAgICAgICMjIyAyLjIuMSBBZGQgb3VyIGVkZ2VzDQogICAgICAuLi4oYWVzKGNvbG91ciA9IC4uLiksIGFycm93PWFycm93KCkpICsNCg0KICAgICAgIyBBZGQgb3VyIG5vZGVzIGFuZCBsYWJlbCB0aGVtDQogICAgICBnZW9tX25vZGVfcG9pbnQoc2l6ZSA9IDcpICsgDQogICAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgc2l6ZSA9IDUsIHJlcGVsID0gVFJVRSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMi4yIEZpbHRlciBhbmQgY2hvb3NlIHlvdXIgZGF0YSBjYXJlZnVsbHkgdG8gYmV0dGVyIHZpc3VhbGl6ZSBpdA0KDQpUaGUgbWV0YWRhdGEgd2UndmUgY2hvc2VuIGlzbid0IHZlcnkgZGV0YWlsZWQgYW5kIHRoYXQgY2FuIGJlIHRoZSBjYXNlIGZvciBtYW55IGRhdGFzZXRzIHNvIHlvdSdsbCB3YW50IHRvIGJlIHN1cmUgb2YgaG93IHlvdSBwaWNrIHlvdXIgbm9kZXMuIFJpZ2h0IG5vdyB3ZSBhcmUgcGlja2luZyBvdXIgYGZyb21gIGFuZCBgdG9gIHZhbHVlcyBiYXNlZCBvbiB0aGUgc3VwcG9zZWQgc291cmNlIGFuZCBpZGVudGlmeWluZyBjb250aW5lbnRzIG9mIHRoZSBzdHJhaW4uDQoNCldoaWxlIG90aGVyIHZhcmlhYmxlcyBjb3VsZCBvZmZlciBtb3JlIGluZm9ybWF0aW9uLCB0aGV5IGNhbiB2YXJ5IGluIGNvbnNpc3RlbmN5IGZyb20gYSByZWdpb24gKEFzaWEpIHRvIGEgcHJvdmluY2UgKE9udGFyaW8pIGFzIHNlZW4gaW4gdmFyaWFibGUgYGNhc2VfbG9jYXRpb25gLiBEZWZpbml0ZWx5IGFpbSB0byBiZSBjb25zaXN0ZW50IHdoZW4geW91J3JlIHdvcmtpbmcgd2l0aCB5b3VyIGRhdGEgb3RoZXJ3aXNlIHRoZSBub2RlcyBtYXkgaGF2ZSBsZXNzIG1lYW5pbmcuDQoNCkxldCdzIHRyeSBsb29raW5nIGF0IGBzb3VyY2VfcmVnaW9uYCAoQ29udGluZW50KSB0byBgY2FzZV9sb2NhdGlvbmAgKERpdmlzaW9ucykgYXMgaXQgcmVsYXRlcyB0byBkYXRhIGZyb20gQ2FuYWRhIGFuZCBBc2lhDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTJ9DQoNCiMgTm93IHdlJ2xsIGFkZCBzb21lIHNwZWNpZmljIHZhcmlhYmxlcyB1c2VkIHRvIGdlbmVyYXRlIG91ciBncmFwaCB0YWJsZQ0KDQpTQzJfZ3JhcGhfaW5mby5kZiAlPiUgDQoNCiAgIyBHZW5lcmF0ZSBvdXIgZnJvbSBhbmQgdG8gZWRnZSBpbmZvcm1hdGlvbg0KICBtdXRhdGUoZnJvbSA9IC4uLiwNCiAgICAgICAgdG8gPSAuLi4NCiAgICAgICAgKSAlPiUgIA0KDQogICMgRmlsdGVyIGZvciBDYW5hZGEgZGF0YQ0KICBmaWx0ZXIoQ291bnRyeSAlaW4lIGMoIkNhbmFkYSIsICJBc2lhIikpICU+JSANCg0KICAjIENvbnZlcnQgdG8gYW4gaWdyYXBoDQogIGFzX3RibF9ncmFwaCgpICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2dyYXBoKC4pICsgDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQoNCiAgICAjIEFkZCBvdXIgZWRnZXMNCiAgICBnZW9tX2VkZ2VfZmFuKGFlcyhjb2xvdXIgPSBOZXh0c3RyYWluKSwgYXJyb3c9YXJyb3coKSkgKw0KICAgICMjIyAyLjIuMiBJZiB3ZSBzdXNwZWN0IGxvb3BpbmcgZWRnZXMsIHdlIG5lZWQgdG8gZGVmaW5lIHRoYXQgbGF5ZXIgc3BlY2lmaWNhbGx5DQogICAgLi4uICsNCg0KICAgICMgQWRkIG91ciBub2RlcyBhbmQgbGFiZWwgdGhlbQ0KICAgIGdlb21fbm9kZV9wb2ludChzaXplID0gNykgKyANCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgc2l6ZSA9IDcsIHJlcGVsID0gVFJVRSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi4zLjAgVGhlcmUgYXJlIG1hbnkgbW9yZSB2aXN1YWxpemF0aW9ucyBmcm9tIGBnZ3JhcGhgDQoNCkludGVyZXN0aW5nbHkgaW4gdGhlIGZpcnN0IHllYXIgb2YgdGhlIHBhbmRlbWljLCBmcm9tIHRoZSBzZXF1ZW5jZWQgZ2Vub21lIGRhdGEsIGluZmVjdGlvbnMgd2l0aGluIENhbmFkYSBhcHBlYXIgdG8gb3JpZ2luYXRlIG1vc3RseSBmcm9tIHdpdGhpbiBOb3J0aCBBbWVyaWNhIGJ1dCB0aGVyZSBhcmUgc29tZSBpbmZlY3Rpb25zIHRoYXQgb3JpZ2luYXRlZCBmcm9tIEFzaWEsIGFuZCBlbnRlcmVkIHRocm91Z2ggT250YXJpbyBhbmQgQnJpdGlzaCBDb2x1bWJpYSAtIG91ciB0d28gbWFpbiBnbG9iYWwgcG9ydHMgb2YgZW50cnkhDQoNCldlJ3ZlIHJlYWxseSBvbmx5IGNvdmVyZWQgbGluZWFyIGdyYXBoIGxheW91dHMgaW4gb3VyIGRhdGEgYnV0IHRoZXJlIGFyZSBhY3R1YWxseSBtYW55ICpraW5kcyogb2YgZ3JhcGhzIHRoYXQgdGhpcyBwYWNrYWdlIGNhbiBwcm9kdWNlLiBUaGUgYGxheW91dGAgcGFyYW1ldGVyIGlzIHRoZSBrZXkgdG8gZXhwbG9yaW5nIGFsbCBvZiB0aGUgZ3JhcGggdHlwZXMgYXZhaWxhYmxlLiBPZiBjb3Vyc2UgeW91ciBkYXRhIGxheW91dCBhbGwgaGFzIHRvIG1ha2Ugc2Vuc2UhIFlvdSdsbCBmaW5kIGdyZWF0IGV4YW1wbGVzIGZyb20gW2RhdGEtaW1hZ2luaXN0LmNvbV0oaHR0cHM6Ly93d3cuZGF0YS1pbWFnaW5pc3QuY29tLzIwMTcvZ2dyYXBoLWludHJvZHVjdGlvbi1sYXlvdXRzLykgbGlrZSB0aGlzIGNpcmNsZXBhY2sgZ3JhcGg6DQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovY2lyY2xlcGFjay5wbmc/cmF3PXRydWUiIHdpZHRoPSI3MDAiLz4NCg0KVGhlcmUgYXJlIGFsbCBraW5kcyBvZiBuZXR3b3JrIGdyYXBocyB0aGF0IGNhbiB1c2UgYWRkaXRpb25hbCBsYXllcnMgb2YgbWV0YWRhdGEgdG8gc2hhcGUgYW5kIHByZXNlbnQgeW91ciBkYXRhDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMy4wLjAgU2VxdWVuY2UgbW90aWZzIGFuZCBnZW5vbWljIG1hcmtlcnMNCg0KV2hlbiB3b3JraW5nIHdpdGggZGF0YSBzb21ldGltZXMgeW91IG1heSBiZSB3b3JraW5nIHdpdGggc2VxdWVuY2Utc3BlY2lmaWMgZGF0YSBmb3IgYW5hbHlzaXMgb2YgbW90aWZzIG9yIHlvdSBtYXkgaGF2ZSBhIGxhcmdlIHNldCBvZiBkYXRhIGRlc2NyaWJpbmcgZ2Vub21pYyBtYXJrZXJzIGxpa2Ugc2luZ2xlIG51Y2xlb3RpZGUgdmFyaWFudHMuIFR3byBwb3B1bGFyIHZpc3VhbGl6YXRpb25zIGluIHRoaXMgZG9tYWluIGFyZSB0aGUgc2VxdWVuY2UgbG9nbyBhbmQgTWFuaGF0dGFuIHBsb3QuDQoNCiMjIDMuMS4wIEdlbmVyYXRlIHNlcXVlbmNlIGxvZ29zIHdpdGggYGdnc2VxbG9nb2ANCg0KVGhpcyBvbmUgaXMgc2hvcnQgYW5kIHNpbXBsZS4gSWYgeW91IGhhdmUgYSBzZXJpZXMgb2Ygc2VxdWVuY2VzIHlvdSdkIGxpa2UgdG8gcGxvdCBiYXNlZCBvbiBmcmVxdWVuY3kgb2YgZWFjaCBiYXNlcyB5b3Ugc2VlIGF0IGVhY2ggcG9zaXRpb24sIHRoZSBzZXF1ZW5jZSBsb2dvIGlzIGZvciB5b3UuIFRoZSBgZ2dzZXFsb2dvYCBwYWNrYWdlIG9mZmVycyBhIHNpbXBsZSBgZ2dwbG90MmAtY29tcGF0aWJsZSBzZXQgb2YgZ2VvbXMgeW91IGNhbiBhZGQgdG8geW91ciBncmFwaCBpbiBvcmRlciB0byByZXByZXNlbnQgdGhpcyBkYXRhLg0KDQpZb3VyIGRhdGEgY2FuIGNvbWUgaW4gdHdvIGZsYXZvdXJzOg0KDQoxLiAgQSBuYW1lZCBsaXN0IHdoZXJlIGVhY2ggaW5kZXggcG9zaXRpb24gaXMgYSB2ZWN0b3Igb2Ygc2VxdWVuY2VzLCBhbmQgdGhlIG5hbWVzIG9mIGVhY2ggaW5kZXggY2FuIGhhdmUgbWVhbmluZyAobGlrZSBhIHRyYW5zY3JpcHRpb24gZmFjdG9yKS4NCg0KMi4gIEEgZnJlcXVlbmN5IG1hdHJpeCB3aGVyZSBjb2x1bW5zIGNvcnJlc3BvbmQgdG8gcG9zaXRpb25zLCBhbmQgcm93bmFtZXMgY29ycmVzcG9uZCB0byBiYXNlcyAob3IgYW1pbm8gYWNpZHMpLiBFYWNoIGVudHJ5IGlzIHRoZSBudW1iZXIgb2YgdGltZXMgdGhhdCBhIGJhc2UgaXMgc2VlbiBhdCBhIHNwZWNpZmljIHBvc2l0aW9uLiBUaGVzZSBtYXRyaWNlcyBjb3VsZCBiZSBkYWlzeS1jaGFpbmVkIGluIGEgbGlzdC4NCg0KTGV0J3Mgd29yayB3aXRoIHRoZSBmb3JtZXIgc2luY2UgaXQgZG9lcyBoYXZlIHNvbWUgZmxleGliaWxpdHkgYW5kIGxvb2tzIGxpa2Ugc29tZXRoaW5nIHdlIHJlY29nbml6ZS4NCg0KYGBge3J9DQojIEJ1aWxkIGFuIGV4YW1wbGUgc2VxdWVuY2Ugc2V0IChpdCdzIGFjdHVhbGx5IGZyb20gdGhlIFNBUlMtQ29WLTIgc3Bpa2Ugc2VxdWVuY2UgKQ0KZXhhbXBsZV9zZXExIDwtIGMoImFhY2NjYWN0YWF0Z2d0Z3R0Z2d0dCIsImFhdGNjYWN0YWF0Z2d0Z3R0Z2N0dCIsImFhY2NjYWF0YWF0YWd0Z3R0Z2d0dCIsDQogICAgICAgICAgICAgICAgICJhYWNjY2FjdGFhdGdndGd0dGdndHQiLCJhYWNjY2FjdGFhdGdndGF0dGdhdHQiLCJhY2NjY2FjdGFhdGd0dGd0dGdndHQiLA0KICAgICAgICAgICAgICAgICAiYWFjY2NhY3RhYXRnZ3RndHRnZ3R0IiwiYWFjY2NhY3RhYXRnZ3RhdHRnYXR0IiwiYWNjY2NhY3RhYXRndHRndHRnZ3R0IikgJT4lIA0KDQogICMgQ29udmVydCBpdCBhbGwgdG8gdXBwZXIgY2FzZSBiZWNhdXNlIGdnc2VxbG9nbyBhcHBlYXJzIHRvIGhhdGUgd2hlbiB3ZSBkb24ndA0KICBzdHJfdG9fdXBwZXIoKQ0KDQpleGFtcGxlX3NlcTIgPC0gYygiY2FjY2NhY3RhYXRnZ3RndHRnZ3RnIiwiYWF0Y2NhY3RhYXRnZ3RndHRnY3R0IiwiYWFjY2NhYXRhYXRhZ3RndHRnZ3R0IiwNCiAgICAgICAgICAgICAgICAgImNhY2NjYWN0YWF0Z2d0Z3R0Z2d0ZyIsImFhY2NjYWN0YWF0Z2d0YXR0Z2F0dCIsImFjY2NjYWN0YWF0Z3R0Z3R0Z2d0dCIsDQogICAgICAgICAgICAgICAgICJjYWNjY2FjdGFhdGdndGd0dGdndGciLCJhYWNjY2FjdGFhdGdndGF0dGdhdHQiLCJhY2NjY2FjdGFhdGd0dGd0dGdndHQiKSAlPiUgDQoNCiAgIyBDb252ZXJ0IGl0IGFsbCB0byB1cHBlciBjYXNlIGJlY2F1c2UgZ2dzZXFsb2dvIGFwcGVhcnMgdG8gaGF0ZSB3aGVuIHdlIGRvbid0DQogIHN0cl90b191cHBlcigpDQoNCiMgQnVpbGQgb3VyIGxpc3QNCmV4YW1wbGVfc2VxLmxpc3QgPSBsaXN0KHNwaWtlX3NldDEgPSAuLi4sIHNwaWtlX3NldDIgPSAuLi4pDQoNCiMgVGFrZSBhIGxvb2sgYXQgb3VyIGxpc3QNCnN0cihleGFtcGxlX3NlcS5saXN0KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4xLjEgUGxvdCB5b3VyIGxvZ28gaW5mb3JtYXRpb24gd2l0aCB0aGUgYGdnc2VxbG9nbygpYCB3cmFwcGVyIGZ1bmN0aW9uDQoNCldlIGhhdmUgdHdvIG9wdGlvbnMgdG8gbG9vayBhdCBvdXIgc2VxdWVuY2UgZGF0YS4gVGhlIGBnZ3NlcWxvZ28oKWAgaXMgYSB3cmFwcGVyIHRoYXQgc2V0cyB1cCB0aGUgZW50aXJlIHBsb3QgZm9yIHVzIGluY2x1ZGluZyBpZiB5b3UnZCBsaWtlIHRvIGZhY2V0IHRoZSBkYXRhLiBJZiB5b3UganVzdCB3YW50IGEgc2luZ2xlIGxvZ28gdGhlbiB5b3UgcGFzcyBwYXJ0cyBvZiB0aGUgbGlzdCBpbmRpdmlkdWFsbHkuDQoNCklmIHlvdSdkIGxpa2UgbXVsdGlwbGUgbG9nb3MsIHlvdSBjYW4gZGVjaWRlZCB0aGVpciBsYXlvdXQgd2l0aCB0aGUgYG5jb2xgIG9yIGBucm93YCBwYXJhbWV0ZXJzLiBUaGUgb3RoZXIgcGFyYW1ldGVycyBhcmU6DQoNCi0gICBgZmFjZXRgIHdoaWNoIHdpbGwgYWNjZXB0IGVpdGhlciAid3JhcCIgb3IgImdyaWQiLg0KDQotICAgYHNjYWxlc2Agd2hpY2ggd2lsbCBhY2NlcHQgdGhlIHNjYWxlIHR5cGVzIGZvciB0aGUgZmFjZXQgZ2VvbS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD00fQ0KDQojIE1ha2UgYSAyLXJvdyBzZXF1ZW5jZSBsb2dvDQpnZ3NlcWxvZ28oLi4uLCAuLi4pICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQogIGxhYnModGl0bGUgPSAiRmFjZXQgaW4gcm93cyIpDQoNCiMgTWFrZSBhIDItY29sdW1uIHNlcXVlbmNlIGxvZ28NCmdnc2VxbG9nbyguLi4sIC4uLikgKw0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsNCiAgbGFicyh0aXRsZSA9ICJGYWNldCBpbiBjb2x1bW5zIikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMS4yIFBsb3QgeW91ciBsb2dvIGluZm9ybWF0aW9uIHdpdGggYGdlb21fbG9nbygpYA0KDQpUbyBwbG90IG91ciBkYXRhIHdlIGNhbiB1c2UgYGdncGxvdGAgbGFuZ3VhZ2UgdGhhdCB3ZSBhcmUgZmFtaWxpYXIgd2l0aCBhbmQgYWRkIGEgbGF5ZXIgdG8gb3VyIHBsb3Qgd2l0aCB0aGUgYGdlb21fbG9nbygpYCBmdW5jdGlvbi4gVGhpcyBsYXllciBpcyBhbHNvIHN1cHBsaWVkIGJ5IHRoZSBgZ2dzZXFsb2dvYCBwYWNrYWdlLiBJbiBvcmRlciBmb3IgdGhpcyB0byBmdW5jdGlvbiBwcm9wZXJseSwgaG93ZXZlciwgd2UgbmVlZCB0byBkZWZpbmUgb3VyIHBhcmFtZXRlcnMgYXM6DQoNCi0gICBgZGF0YWA6IHRoZSB2ZWN0b3Igd2l0aCBvZiBvdXIgc2VxdWVuY2UgZGF0YQ0KDQotICAgYHNlcV90eXBlYDogc2V0cyB0aGUgdHlwZSBvZiBzZXF1ZW5jZSBsaWtlICJETkEiLCAiUk5BIiBvciAiQUEiDQoNCi0gICBgbWV0aG9kYDogdGhlIHktc2NhbGUgb2Ygb3VyIGxvZ28gZWl0aGVyIGluICJiaXRzIiAoaW5mb3JtYXRpb24gdmFsdWUpIG9yIGJ5ICJwcm9iYWJpbGl0eSINCg0KVGhlc2UgYW5kIG90aGVyIHBhcmFtZXRlcnMgY2FuIGFsc28gYmUgc2V0IGluIHRoZSBgZ2dzZXFsb2dvKClgIGZ1bmN0aW9uIHRvby4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD00fQ0KDQojIDEuIERhdGENCmdncGxvdCgpICsgDQogICMgMi4gQWVzdGhldGljcw0KICB0aGVtZV9sb2dvKCkgKyANCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQogIGxhYnModGl0bGUgPSAiRmFjZXQgaW4gY29sdW1ucyB3aXRoIGdlb21fbG9nbygpIikgKw0KDQogICMgNC4gR2VvbXMNCiAgZ2VvbV9sb2dvKGRhdGEgPSBleGFtcGxlX3NlcS5saXN0LCBzZXFfdHlwZT0iRE5BIiwgbWV0aG9kID0gImJpdHMiKSArIA0KICANCiAgIyA2LiBGYWNldA0KICBmYWNldF93cmFwKC4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4yLjAgVGhlIE1hbmhhdHRhbiBwbG90IGZvciBhbGwgeW91ciBnZW5vbWUtcG9zaXRpb24gdmlzdWFsaXphdGlvbiBuZWVkcw0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L01hbmhhdHRhbl9leGFtcGxlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KDQpUaGUgTWFuaGF0dGFuIHBsb3QgaXMgdXN1YWxseSB1c2VkIGluIHZpc3VhbGl6aW5nIGRhdGEgYWNyb3NzIHRoZSBnZW5vbWUgZnJvbSBMT0Qgc2NvcmVzIHRvIG1hcmtlciBwcm9wb3J0aW9ucy4NCjo6Og0KDQpOYW1lZCBmb3IgaXQncyB2aXN1YWwgc2ltaWxhcml0eSB0byB0aGUgTWFuaGF0dGFuIHNreWxpbmUgb2Ygc2luZ3VsYXIgdG93ZXJpbmcgc2t5c2NyYXBlcnMgc2NhdHRlcmVkIGFib3ZlIGxvdy1sZXZlbCBidWlsZGluZ3MsIHRoaXMgcGxvdCBpcyBjb21tb25seSB1c2VkIGZvciB2aXN1YWxpemluZyBnZW5vbWljIG1hcmtlcnMgYWNyb3NzIGFuIG9yZGVyZWQgbGluZWFyIGF4aXMgbGlrZSBhIHNlcmllcyBvZiBjaHJvbW9zb21lcy4gVGhlIHktYXhpcyBvZiB0aGUgdmFsdWVzIGNhbiByZXByZXNlbnQgTE9EIHNjb3JlcyBvciBwcm9wb3J0aW9ucyBvZiBtYXJrZXIgcmVwcmVzZW50YXRpb24uIFRoaXMgaXMgZnJlcXVlbnRseSB1c2VkIHRvIHZpc3VhbGl6ZSBHV0FTIG9yIG1hcHBpbmcgZGF0YS4NCg0KVGhpcyBpcyBhbm90aGVyIHNjYXR0ZXJwbG90IHRoYXQgd2UgY2FuIGdlbmVyYXRlIGRpcmVjdGx5IGluIGBnZ3Bsb3RgIHdpdGggc29tZSBlZmZvcnQvc2V0dXAgb3Igd2UgY2FuIHVzZSBhIHBhY2thZ2UgbGlrZSBgcXFtYW5gLg0KDQpGaXJzdCBsZXQncyBsb29rIGF0IHRoZSBraW5kIG9mIGRhdGEuIEZ1biBmYWN0ISBZb3UgY2FuIHJlYWQgaW4gYXJjaGl2ZWQvemlwcGVkIGRhdGEgYXMgd2VsbCwgYWx0aG91Z2ggYmUgY2FyZWZ1bCwgR1dBUyBmaWxlcyBjYW4gYmUgcXVpdGUgbGFyZ2UhDQoNCmBgYHtyfQ0KbG9hZCguLi4pDQoNCnN0ciguLi4sIGdpdmUuYXR0ciA9IEZBTFNFKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4yLjEgVXNlIHRoZSBgbWFuaGF0dGFuKClgIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIGEgcGxvdA0KDQpUbyBnZW5lcmF0ZSBvdXIgTWFuaGF0dGFuIHBsb3Qgd2UgY2FuIHVzZSB0aGUgYG1hbmhhdHRhbigpYCB3cmFwcGVyIGZ1bmN0aW9uIHdoaWNoIHdpbGwgcmVxdWlyZSBmb3VyIHNldHMgb2YgcGFyYW1ldGVyIGluZm9ybWF0aW9uOg0KDQotICAgYGNocmA6IHRoZSBjb2x1bW4gbmFtZSB3aXRoIHRoZSBjaHJvbW9zb21lIGluZm9ybWF0aW9uIGZvciBhIGdpdmVuIFNOUC4NCg0KLSAgIGBicGA6IHRoZSBiYXNlcGFpciBwb3NpdGlvbiBvZiB0aGUgU05QLg0KDQotICAgYHNucGA6IHRoZSBJRCBvZiB0aGUgU05QIHVzdWFsbHkgYSBSZWZTTlAgSUQgKFJTSUQpIG9mIHNvbWUga2luZC4NCg0KLSAgIGBwYDogdGhlIHAtdmFsdWUgZm9yIHRoYXQgcGFydGljdWxhciBTTlAgZ2VuZXJhdGVkIGZyb20gdGhlIGNhc2UgdnMuIGNvbnRyb2wgYW5hbHlzaXMuIFRoaXMgd2lsbCBiZSBjb252ZXJ0ZWQgdG8gYSAkLWxvZzEwKHApJCB2YWx1ZSBmb3IgdGhlIHktYXhpcy4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBtYW5oYXR0YW4oZ3dhc1Jlc3VsdHMsIGNocj0iQ0hSIiwgYnA9IkJQIiwgc25wPSJTTlAiLCBwPSJQIikNCg0KR1dBU19kYXRhLmRmICU+JSANCiMgWW91IGNhbiBmaWx0ZXIgeW91ciBkYXRhIGlmIHlvdSB3YW50IHRoaXMgc3RlcCB0byBydW4gZmFzdGVyDQojIGZpbHRlcihDSFIgJWluJSBjKDE6NSkpICU+JSAgDQoNCiAgLi4uKC4sIGNocj0iQ0hSIiwgYnA9IlBPUyIsIHNucD0icnNpZCIsIHA9InAudmFsdWUiKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4yLjIgVXNlIHRoZSBgaGlnaGxpZ2h0YCBwYXJhbWV0ZXIgdG8gaWRlbnRpZnkgbWFya2VycyBvZiBpbnRlcmVzdA0KDQpOb3cgdGhhdCB3ZSd2ZSBwbG90dGVkIG91ciBNYW5oYXR0YW4gcGxvdCwgd2UgY2FuIHNlZSBzb21lIGhvcml6b250YWwgbGluZXMgY29ycmVzcG9uZGluZyB0byBtaW5pbXVtIHAtdmFsdWVzIG9mIDEuMCB4IDEwXi01XiBhbmQgNS4wIHggMTBeLTheIGFsdGhvdWdoIHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlc2UgY2FuIGRlcGVuZCBvbiBzYW1wbGUgc2l6ZSBhbmQgYWxsZWxlIGZyZXF1ZW5jaWVzIG9mIHlvdXIgU05Qcy4NCg0KRWl0aGVyIHdheSwgeW91IGNhbiBoaWdobGlnaHQgU05QIHJlc3VsdHMgYmFzZWQgb24gYSBwcm92aWRlZCBsaXN0LiBJbiB0aGlzIGNhc2UsIHdlIGNhbiBnZW5lcmF0ZSBhIGxpc3Qgb3Vyc2VsdmVzIGJ5IGZpbHRlcmluZyBvbiB0aGUgYHAudmFsdWVgIHZhcmlhYmxlLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQpzbnBfY2FuZGlkYXRlcyA8LSANCg0KICAjIFBhc3MgYWxvbmcgb3VyIEdXQVMgZGF0YQ0KICBHV0FTX2RhdGEuZGYgJT4lIA0KDQogICMgRmlsdGVyIGl0IGZvciBsb3cgcC12YWx1ZXMNCiAgZmlsdGVyKC1sb2cxMChwLnZhbHVlKSA+PSA1KSAlPiUgDQoNCiAgIyBTZWxlY3QgU05QIGNhbmRpZGF0ZXMNCiAgcHVsbCguLi4pICU+JSANCiAgdW5pcXVlKCkNCg0KIyBzbnBfY2FuZGlkYXRlcw0KDQojIFBsb3Qgb3VyIE1hbmhhdHRhbiBwbG90DQptYW5oYXR0YW4oR1dBU19kYXRhLmRmLCANCiAgICAgICAgICBjaHI9IkNIUiIsIA0KICAgICAgICAgIGJwPSJQT1MiLCANCiAgICAgICAgICBzbnA9InJzaWQiLCANCiAgICAgICAgICBwPSJwLnZhbHVlIiwgDQogICAgICAgICAgaGlnaGxpZ2h0ID0gLi4uKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDQuMC4wIFRpcHMgYW5kIFRyaWNrcw0KDQpXaGVuIHByZXBhcmluZyBmaWd1cmVzIGZvciBhIHByZXNlbnRhdGlvbiBvciBtYW51c2NyaXB0IGNvbnNpZGVyIHNvbWUgb2YgdGhlIGZvbGxvd2luZyB0aXBzL3RyaWNrcyB0aGF0IEkndmUgYWNjdW11bGF0ZWQgb3ZlciB0aGUgeWVhcnMuIE5vdCBhbGwgbWF5IGFwcGx5IHRvIHlvdXIgd29yayBidXQgdGhlc2UgY2FuIGNlcnRhaW5seSBiZSBoZWxwZnVsLg0KDQojIyA0LjEuMCBIb3cgbWFueSBmaWd1cmVzIGRvIHlvdSBuZWVkIHRvIHRlbGwgeW91ciBzdG9yeT8NCg0KUmVnYXJkbGVzcyBvZiB3aGV0aGVyIG9yIG5vdCB5b3UnbGwgYmUgZ2l2aW5nIGEgdGFsayBvciBzdWJtaXR0aW5nIGEgbWFudXNjcmlwdCB5b3UnbGwgd2FudCB0byBjb25zaWRlciBob3cgbWFueSBmaWd1cmVzIHlvdSBuZWVkIHRvIHRlbGwgeW91ciBzdG9yeS4NCg0KLSAgICoqTW9zdCBtYWpvciBwdWJsaWNhdGlvbnMqKiB3aWxsIGhhdmUgNC02IG1haW4gZmlndXJlcyB3aXRoIHVzdWFsbHkgYW4gKHVubGltaXRlZCBub3dhZGF5cykgbnVtYmVyIG9mIHN1cHBsZW1lbnRhbCBmaWd1cmVzLg0KDQogICAgLSAgIE1haW4gZmlndXJlcyBjYW4gdXN1YWxseSBhY2NvbW9kYXRlIHVwIHRvIDQtOCBzdWJwYW5lbHMgZGVwZW5kaW5nIG9uIHRoZSBjb21wbGV4aXR5IG9mIHlvdXIgZGF0YS9zdWJqZWN0DQoNCi0gICAqKlByZXNlbnRhdGlvbnMqKiBnZW5lcmFsbHkgaGF2ZSAxIHNsaWRlIHBlciBtaW51dGUgb2YgdGFsayBzbyBwbGFuIGFjY29yZGluZ2x5Lg0KDQogICAgLSAgIEEgY29tcGxpY2F0ZWQgZmlndXJlIG9yIGZpZ3VyZSB0eXBlIHNob3VsZCBiZSBnaXZlbiBzb21lIGV4dHJhIHRpbWUgdG8gaW5pdGlhbGx5IGV4cGxhaW4gYnV0IGlmIHlvdSBhcmUgdXNpbmcgdGhlIGZvcm1hdCBvZnRlbiwgbGF0ZXIgdmVyc2lvbnMgd2lsbCBiZSBzaW1wbGVyIHRvIGRpZ2VzdC4gU3BlbmQgdGltZSBkaXNjdXNzaW5nIHRoZSBmaXJzdCBvY2N1cnJlbmNlIG9mIGEgZmlndXJlIHR5cGUgdG8gYnVpbGQgYSBzb2xpZCBmb3VuZGF0aW9uIQ0KDQojIyA0LjIuMCBNYW51c2NyaXB0IHNwZWNpZmljcw0KDQpXaGlsZSBwdWJsaWNhdGlvbiByZXF1aXJlbWVudHMgdmFyeSBmcm9tIGpvdXJuYWwgdG8gam91cm5hbCwgbW9zdCBoYXZlIHNpbWlsYXIgcnVsZXMNCg0KLSAgIE1pbmltdW0gZHBpIChyZXNvbHV0aW9uKSBvZiB5b3VyIGltYWdlczogMzAwZHBpDQoNCi0gICBNaW5pbXVtIGZvbnQgc2l6ZSAoaWUgdGhlIHZlcnkgc21hbGxlc3QgYW55IGZvbnQgc2hvdWxkIGJlIG9uIGEgc3ViIHBhbmVsKTogVXN1YWxseSA2LTdwdHMNCg0KLSAgIE1heGltdW0gaW1hZ2Ugc2l6ZS4gVGhlcmUgaXMgdXN1YWxseSBhIG1heGltdW0gZmlndXJlIHNpemUgaW4gdGVybXMgb2YgZGltZW5zaW9ucyBidXQgdGhpcyByZWFsbHkgY2FuIHZhcnkgd2lkZWx5LiBDaGVjayB3aXRoIGEgZmV3IHBvdGVudGlhbCBwdWJsaWNhdGlvbnMgZmlyc3QgdG8gZ2V0IGFuIGlkZWEgb2YgdGhlIG1haW4gc2l6ZS9sYXlvdXQgKHBvcnRyYWl0IHZzIGxhbmRzY2FwZSkgZm9yIGZpZ3VyZXMuDQoNCi0gICBLZWVwIEFMTCBmaWd1cmUgZGF0YSBvcmdhbml6ZWQgZm9yIHN1Ym1pc3Npb24hIFRoaXMgaXMgdGhlIHN0YW5kYXJkIG5vdyBhbmQgY2FuIGluY2x1ZGUgc2VxdWVuY2VzLCBvciBhbnkgZGF0YSB0aGF0IHdhcyB1c2VkIHRvIGNyZWF0ZSB5b3VyIGZpZ3VyZXMuIFlvdSBjYW4gb3JnYW5pemUgdGhpcyBpbiBhIHNpbmdsZSBDU1YsIG9yIHdvcmtzaGVldC1iYXNlZCBmaWxlLg0KDQotICAgRmlndXJlcyBzaG91bGQgYmUgZGVjaXBoZXJhYmxlIGF0IGEgZ2xhbmNlLiBBIHN1Y2Nlc3NmdWwgZmlndXJlJ3MgZ2VuZXJhbCBtZXNzYWdlIGNhbiBiZSB1bmRlcnN0b29kIHdpdGhvdXQgaGF2aW5nIHRvIHJlYWQgdGhyb3VnaCB0aGUgZmlndXJlIGxlZ2VuZHMuIFRoaXMgaXMgbm90ICphbHdheXMqIHBvc3NpYmxlIGJ1dCBpcyBjZXJ0YWlubHkgc29tZXRoaW5nIHRvIHN0cml2ZSBmb3IuDQoNCiAgICAtICAgRmlndXJlIHRpdGxlcyBhcmUgb3B0aW9uYWwuIE9mdGVuIGhlbHBmdWwgZm9yIHRoZSBhYm92ZSBwb2ludCBidXQgeW91IHNhY3JpZmljZSBzcGFjZSB0byBoYXZlIHRoZW0hDQoNCiMjIDQuMy4wIFByZXNlbnRhdGlvbiBmaWd1cmVzDQoNCi0gICBNYWtlIHlvdXIgZm9udCBiaWcgZW5vdWdoIHRvIHJlYWQuIFVubGlrZSBhIG1hbnVzY3JpcHQsIHBlb3BsZSBjYW5ub3Qgem9vbSBpbiBvbiB5b3VyIHByZXNlbnRhdGlvbiBmaWd1cmVzDQoNCi0gICBMaW1pdCBwYW5lbHMgdG8gMi00IGF0IG1vc3QgcGVyIGZpZ3VyZS4NCg0KLSAgIEZvcmdldCBhYm91dCB0aGUgdGl0bGUgb24geW91ciBmaWd1cmUuIFRpdGxlIHRoZSAqKipzbGlkZSoqKiBpbnN0ZWFkIHdpdGggeW91ciB0YWtlLWhvbWUgbWVzc2FnZSBhYm91dCB0aGUgZmlndXJlLiBIYXZpbmcgYW4gZWZmZWN0aXZlIHNsaWRlIHRpdGxlIHRlbGxzIHlvdXIgYXVkaWVuY2Ugd2hlcmUgdG8gZm9jdXMgb24geW91ciBmaWd1cmUuIFRoaXMgaXMgKmVzcGVjaWFsbHkga2V5KiB3aGVuIHlvdSBhcmUgcHJlc2VudGluZyBhIGNvbXBsaWNhdGVkIG9yICJidXN5IiBmaWd1cmUuDQoNCi0gICBXaGVuIHByZXNlbnRpbmcgYSBmaWd1cmUgd2l0aCBtdWx0aXBsZSBjYXRlZ29yaWVzIG9yIGlkZWFzLCByZXZlYWwgaXQgcGllY2VtZWFsLiBQcmVzZW50IHRoZSBiYXNlIGlkZWEgKGllIGEgY29udHJvbCkgYW5kIHRoZW4gcmV2ZWFsIHRoZSBuZXh0IHBhcnQgb2YgdGhlIHBhbmVsIHNob3dpbmcgeW91ciBmaXJzdCBleHBlcmltZW50YWwgY29uZGl0aW9uLCB0aGVuIHlvdXIgc2Vjb25kIGV0Yy4gVGhpcyBhbGxvd3MgdGhlIGF1ZGllbmNlIHRvIGFjY2xpbWF0ZSB0byB0aGUgaWRlYSB5b3Ugd2lzaCB0byBjb252ZXkuDQoNCi0gICBJZiB5b3UgY2FuJ3QgcmVhZCB0aGUgdGV4dCBvZiBhIGZpZ3VyZSBiZWNhdXNlIHRoZXJlIGFyZSBqdXN0IHRvbyBtYW55IHRoaW5ncywgY29uc2lkZXIgc2ltcGxpZnlpbmcgb3IgcmVtb3ZpbmcgdGhlIHRleHQuIFRoaXMgd2lsbCByZWR1Y2Ugb24gZGlzdHJhY3RpbmcgaW1hZ2VyeSBhbmQgZm9jdXMgeW91ciBtZXNzYWdlIHRvIHlvdXIgYXVkaWVuY2UuDQoNCiMjIDQuNC4wIFRoaW5ncyB0byB0aGluayBhYm91dCB3aGVuIGNvZGluZw0KDQotICAgS2VlcCBhIHNjcmlwdCAob3Igbm90ZWJvb2shKSBmb3IgcmVtYWtpbmcgYWxsIHlvdXIgZmlndXJlcyENCg0KICAgIC0gICBTaW1wbGlmeSB3aXRoIGEgZmV3IHZhcmlhYmxlcyB0aGluZ3MgbGlrZSB0aGUgbG9jYXRpb24gb2YgeW91ciBkYXRhc2V0cyENCg0KICAgIC0gICBCdWlsZCBmdW5jdGlvbnMgdG8gbWFrZSBmaWd1cmVzIG9mIHRoZSBzYW1lIHR5cGUgd2l0aCBkaWZmZXJlbnQgZGF0YXNldHMuDQoNCiAgICAtICAgQ3JlYXRlIGEgbWFzdGVyIGZpbGUvdGFibGUgdG8gaW1wb3J0IGFuZCB1c2UgdGhlc2UgZGF0YXNldHMgYW5kIGZ1bmN0aW9ucy4NCg0KLSAgIENyZWF0ZSBhIHNwZWNpZmljIGNvbG91ci1zY2hlbWUgZm9yIHlvdXIgZmlndXJlcw0KDQogICAgLSAgIFRyeSB0byB1c2UgdGhlIHNhbWUgY29sb3VyIGZvciBjb250cm9scyB2ZXJzdXMgc3BlY2lmaWMgbXV0YW50IGdlbm90eXBlcyB5b3UgbWF5IGJlIGV4YW1pbmluZyBvciByZS11c2luZyBmcmVxdWVudGx5DQoNCiAgICAtICAgVXNlIGEgY29sb3VyYmxpbmQtZnJpZW5kbHkgcGFsZXR0ZQ0KDQotICAgU2F2ZSB5b3VyIHBsb3RzIGFzIFNWRywgUE5HIG9yIFRJRkZzDQoNCiAgICAtICAgU1ZHcyBhcmUgdmVjdG9yaXplZCBtZWFuaW5nIHRoZXkgYXJlIGZpbGVzIHJlcHJlc2VudGluZyBsaW5lcywgY3VydmVzLCBhbmQgc2hhcGVzIGJhc2VkIG9uIGEgY29vcmRpbmF0ZSBzeXN0ZW0uIFRoaXMgbWVhbnMgdGhleSBhcmUgZ2VuZXJhbGx5IHJlc29sdXRpb25sZXNzIGFuZCBjYW4gYmUgYmxvd24gdXAgdG8gYW55IHNpemUgYXMgbmVlZGVkLiBHcmVhdCBmb3Igd29ya2luZyB3aXRoIG9uIHByZXNlbnRhdGlvbnMgYW5kIHNpbXBsZSB0byBhbHRlciBpbiBtb3N0IGltYWdlIGVkaXRpbmcgcHJvZ3JhbXMuIElkZWFsIGZvciB1c2luZyBvbiBncmFwaHMgYW5kIGdncGxvdCBmaWd1cmVzIGJ1dCBub3QgbWljcm9zY29weSBvciBvdGhlciBkaWdpdGFsIGltYWdlcy4NCg0KICAgIC0gICBBdm9pZCBKUEVHcyBhcyBtdWNoIGFzIHBvc3NpYmxlLiBUaGUgdGVuZCB0byBoYXZlIHBvb3IgKGxvc3N5KSBlbmNvZGluZyB0aGF0IGNhbiBjcmVhdGUgcmVzb2x1dGlvbiBsb3NzIHVubGVzcyB5b3Ugc2F2ZSB0aGVtIGFzIGJpZyBoaWdoLXF1YWxpdHkgSlBFRyB2ZXJzaW9ucyAobG9zc2xlc3MpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA1LjAuMCBDbGFzcyBTdW1tYXJ5DQoNCldpdGggdGhpcyBmaW5hbCBsZWN0dXJlIHdlJ3ZlIGNvdmVyZWQgdGhlIHNwZWN0cnVtIG9mIHZpc3VhbGl6YXRpb25zIGNvdmVyaW5nIHRoZSBtb3N0IGJhc2ljIG9mIHNjYXR0ZXItIGFuZCBiYXJwbG90cywgZ3JhZHVhdGluZyB0byBib3hwbG90cywgdmlvbGluIHBsb3RzLCBhbmQgdGhlaXIgdmFyaWFudHMuIFdlJ3ZlIGNvdmVyZWQgaGlnaC10aHJvdWdocHV0IGRhdGEgdmlzdWFsaXphdGlvbnMgaW5jbHVkaW5nIHZvbGNhbm8gcGxvdHMsIGhlYXRtYXBzLCBhbmQgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcy4gRnVydGhlcm1vcmUgd2UgbG9va2VkIGF0IGhvdyBzaW1wbGUgdGhlIHByb2plY3Rpb24gb2YgaGlnaC1kaW1lbnNpb24gZGF0YSBjYW4gYmUgd2l0aCB0LVNORSBhbmQgVU1BUC4NCg0KV2UgZmluaXNoZWQgb3VyIGNvdXJzZSB0b2RheSBjb3ZlcmluZyBwaHlsb2dlbmV0aWMgdHJlZXMsIG5ldHdvcmsgZ3JhcGhzLCBhbmQgc29tZSBzZXF1ZW5jZSBhbmFseXNpcyB2aXN1YWxpemF0aW9ucy4gT3ZlcmFsbCB5b3Ugbm93IGhhdmUgdGhlIHRvb2xzIHRvIHdyYW5nbGUgZGF0YSB0aGF0IG1heSBhcHBlYXIgaW4gYWxsIHNvcnRzIG9mIGZvcm1hdHMgYWxvbmcgd2l0aCBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIHdoZW4gYW5kIGhvdyB0byBwcmVwYXJlIHNvbWUgb2YgdGhlIG1vc3QgY29tbW9uIGRhdGEgdmlzdWFsaXphdGlvbnMgaW4geW91ciBidXJnZW9uaW5nIHNjaWVuY2UgY2FyZWVycy4NCg0KQ29uZ3JhdHVsYXRpb25zIGFuZCBnb29kIGx1Y2sgb24geW91ciBkYXRhIHNjaWVuY2Ugam91cm5leSENCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA1LjAuMSBQb3N0LWNvdXJzZSBzdXJ2ZXkNCg0KV2UgaGF2ZSBjcmVhdGVkIGEgcG9zdC1jb3Vyc2Ugc3VydmV5IHlvdSBjYW4gZmlsbCBvdXQgYW5vbnltb3VzbHkuIFlvdSBjYW4gdXNlIHRoaXMgc3VydmV5IGFzIGFuIG9wcG9ydHVuaXR5IHRvIHRlbGwgdXMgYWJvdXQgeW91ciBleHBlcmllbmNlIGFuZCBoZWxwIHNoYXBlIHRoZSBmdXR1cmUgb2ZmZXJpbmdzIG9mIHRoaXMgc2VyaWVzLiBQbGVhc2UgdGFrZSA1LTEwIG1pbnV0ZXMgdG8gZmlsbCBvdXQgdGhlIHN1cnZleS4gV2UgcmVhbGx5IGFwcHJlY2lhdGUgeW91ciBmZWVkYmFjayENCg0KW0Fub255bW91cyBHb29nbGUgU3VydmV5IGZvdW5kIGhlcmVdKGh0dHBzOi8vZm9ybXMuZ2xlL0FhWWt5cnd0dVhUeWFhVUZBKQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS4xLjAgV2Vla2x5IGFzc2lnbm1lbnQNCg0KVGhpcyB3ZWVrJ3MgYXNzaWdubWVudCB3aWxsIGJlIGZvdW5kIHVuZGVyIHRoZSBjdXJyZW50IGxlY3R1cmUgZm9sZGVyIHVuZGVyIHRoZSAiYXNzaWdubWVudCIgc3ViZm9sZGVyLiBJdCB3aWxsIGluY2x1ZGUgYW4gUiBtYXJrZG93biBub3RlYm9vayB0aGF0IHlvdSB3aWxsIHVzZSB0byBwcm9kdWNlIHRoZSBjb2RlIGFuZCBhbnN3ZXJzIGZvciB0aGlzIHdlZWsncyBhc3NpZ25tZW50LiBQbGVhc2UgcHJvdmlkZSBhbnN3ZXJzIGluIG1hcmtkb3duIG9yIGNvZGUgY2VsbHMgdGhhdCBpbW1lZGlhdGVseSBmb2xsb3cgZWFjaCBxdWVzdGlvbiBzZWN0aW9uLg0KDQp8ICAgICAgICAgICAgICAgICAgICB8IEFzc2lnbm1lbnQgYnJlYWtkb3duIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgICAgICAgIENvZGUgICAgICAgIHwgICAgICAgICA1MCUgICAgICAgICAgfCBcLSBEb2VzIGl0IGZvbGxvdyBiZXN0IHByYWN0aWNlcz8gICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gRG9lcyBpdCBtYWtlIGdvb2QgdXNlIG9mIGF2YWlsYWJsZSBwYWNrYWdlcz8gfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIFdhcyBkYXRhIHByZXBhcmVkIHByb3Blcmx5ICAgICAgICAgICAgICAgICAgIHwNCnwgQW5zd2VycyBhbmQgT3V0cHV0IHwgICAgICAgICA1MCUgICAgICAgICAgfCBcLSBJcyBvdXRwdXQgYmFzZWQgb24gdGhlIGNvcnJlY3QgZGF0YXNldD8gICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gQXJlIGdyb3VwaW5ncyBhcHByb3ByaWF0ZSAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIEFyZSBjb3JyZWN0IHRpdGxlcy9heGVzL2xlZ2VuZHMgY29ycmVjdD8gICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBJcyBpbnRlcnByZXRhdGlvbiBvZiB0aGUgZ3JhcGhzIGNvcnJlY3Q/ICAgICB8DQoNClNpbmNlIGNvZGluZyBzdHlsZXMgYW5kIHNvbHV0aW9ucyBjYW4gZGlmZmVyLCBzdHVkZW50cyBhcmUgZW5jb3VyYWdlZCB0byB1c2UgYmVzdCBwcmFjdGljZXMuIEFzc2lnbm1lbnRzICptYXkqIGJlIHJld2FyZGVkIGZvciB3ZWxsLWNvZGVkIG9yIGVsZWdhbnQgc29sdXRpb25zLg0KDQpZb3UgY2FuIHNhdmUgYW5kIGRvd25sb2FkIHRoZSBtYXJrZG93biBub3RlYm9vayBpbiBpdHMgbmF0aXZlIGZvcm1hdC4gU3VibWl0IHRoaXMgZmlsZSB0byB0aGUgdGhlIGFwcHJvcHJpYXRlIGFzc2lnbm1lbnQgc2VjdGlvbiBieSAxMjo1OSBwbSBvbiB0aGUgZGF0ZSBvZiBvdXIgbmV4dCBjbGFzczogQXByaWwgMjV0aCwgMjAyNC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuMi4wIEFja25vd2xlZGdlbWVudHMNCg0KKipSZXZpc2lvbiAxLjAuMCoqOiBjcmVhdGVkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIxSCBTIExFQzAxNDEqKiwgMDMtMjAyMSBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQoqKlJldmlzaW9uIDEuMC4xKio6IGVkaXRlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMEggUyBMRUMwMTQxKiosIDAzLTIwMjIgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KKipSZXZpc2lvbiAxLjAuMioqOiBlZGl0ZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDIzIGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCioqUmV2aXNpb24gMi4wLjAqKjogUmV2aXNlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMEggUyBMRUMwMTQxKiosIDAzLTIwMjQgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuMy4wIFJlZmVyZW5jZXMNCg0KVGhlIGJvb2sgb2YgYGdndHJlZWAgZnJvbSB0aGUgWXUgTGFiOiA8aHR0cHM6Ly95dWxhYi1zbXUudG9wL3RyZWVkYXRhLWJvb2svaW5kZXguaHRtbD4NCg0KQ2hhcHRlciAxMCBvZiBgZ2d0cmVlYCB3aXRoIGFtYXppbmcgYW5kIGNvbXBsaWNhdGVkIHBsb3RzOiA8aHR0cHM6Ly95dWxhYi1zbXUudG9wL3RyZWVkYXRhLWJvb2svY2hhcHRlcjEwLmh0bWw+DQoNCk1vcmUgaW5mb3JtYXRpb24gb24gdGhlIGBnZ3JhcGhgIHBhY2thZ2U6IDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dyYXBoL2dncmFwaC5wZGY+DQoNCmBnZ3NlcWxvZ29gIHBhY2thZ2UgaW5mb3JtYXRpb246IDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dzZXFsb2dvL2dnc2VxbG9nby5wZGY+DQoNCk1hbmhhdHRhbiBwbG90IHR1dG9yaWFsOiA8aHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8xMDFfTWFuaGF0dGFuX3Bsb3QuaHRtbD4NCg0KR1dBUyBwLXZhbHVlIHRocmVzaG9sZDogPGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxMzgwLTAyMC0wNjcwLTM/cHJvb2Y9dD4NCg0KTW9yZSBvbiBHV0FTIHRocmVzaG9sZHMgZm9yIHNpZ25pZmljYW5jZTogPGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvZWpoZzIwMTUyNjkucGRmPg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgVGhlIENlbnRlciBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikNCg0KVGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikgYXQgdGhlIFVuaXZlcnNpdHkgb2YgVG9yb250byBvZmZlcnMgY29tcHJlaGVuc2l2ZSBleHBlcmltZW50YWwgZGVzaWduLCByZXNlYXJjaCwgYW5kIGFuYWx5c2lzIHNlcnZpY2VzIGluIG1pY3JvYmlvbWUgYW5kIG1ldGFnZW5vbWljIHN0dWRpZXMsIGdlbm9taWNzLCBwcm90ZW9taWNzLCBhbmQgYmlvaW5mb3JtYXRpY3MuDQoNCkZyb20gdGFyZ2V0ZWQgRE5BIGFtcGxpY29uIHNlcXVlbmNpbmcgdG8gdHJhbnNjcmlwdG9tZXMsIHdob2xlIGdlbm9tZXMsIGFuZCBtZXRhZ2Vub21lcywgZnJvbSBwcm90ZWluIGlkZW50aWZpY2F0aW9uIHRvIHBvc3QtdHJhbnNsYXRpb25hbCBtb2RpZmljYXRpb24sIENBR0VGIGhhcyB0aGUgdG9vbHMgYW5kIGtub3dsZWRnZSB0byBzdXBwb3J0IHlvdXIgcmVzZWFyY2guIE91ciBzdGF0ZS1vZi10aGUtYXJ0IGZhY2lsaXR5IGFuZCBleHBlcmllbmNlZCByZXNlYXJjaCBzdGFmZiBwcm92aWRlIGEgYnJvYWQgcmFuZ2Ugb2Ygc2VydmljZXMsIGluY2x1ZGluZyBib3RoIHN0YW5kYXJkIGFuYWx5c2VzIGFuZCB0ZWNobmlxdWVzIGRldmVsb3BlZCBieSBvdXIgdGVhbS4gSW4gcGFydGljdWxhciwgd2UgaGF2ZSBzcGVjaWFsIGV4cGVydGlzZSBpbiBtaWNyb2JpYWwsIHBsYW50LCBhbmQgZW52aXJvbm1lbnRhbCBzeXN0ZW1zLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB1cyBhbmQgdGhlIHNlcnZpY2VzIHdlIG9mZmVyLCBwbGVhc2UgdmlzaXQgPGh0dHBzOi8vd3d3LmNhZ2VmLnV0b3JvbnRvLmNhLz4uDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovQ0FHRUZfbmV3LnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQo=